<!DOCTYPE html>
<html lang="zh-Hans">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!--Description-->
    
        <meta name="description" content="Note">
    

    <!--Author-->
    
        <meta name="author" content="兰陵忧患生">
    

    <!-- Title -->
    
    <title>JSP&amp;Servlet 学习笔记 | Lan</title>

    <!-- Bootstrap Core CSS -->
    <link href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/styles/default.min.css">
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/highlight.min.js"></script>

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/style.css">

    <!-- Custom Fonts -->
    <link href="//cdn.bootcss.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>

<body>

    <!-- Content -->
    <section class="article-container">
<!-- Back Home -->
<a class="nav-back" href="/">
    <i class="fa fa-puzzle-piece"></i>
</a>

<!-- Page Header -->
<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <h1>JSP&Servlet 学习笔记</h1>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- TOC -->
            <div class="toc-article col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Web应用程序简介"><span class="toc-text">Web应用程序简介</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#URL"><span class="toc-text">URL</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HTTP"><span class="toc-text">HTTP</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#编写与设置servlet"><span class="toc-text">编写与设置servlet</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#在HelloServlet之后"><span class="toc-text">在HelloServlet之后</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用-WebServlet"><span class="toc-text">使用@WebServlet</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用Web-xml"><span class="toc-text">使用Web.xml</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#文件组织与部署"><span class="toc-text">文件组织与部署</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#进阶部署设置"><span class="toc-text">进阶部署设置</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#URL模式设置"><span class="toc-text">URL模式设置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Web目录结构"><span class="toc-text">Web目录结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用web-fragment-xml"><span class="toc-text">使用web-fragment.xml</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#请求与响应"><span class="toc-text">请求与响应</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#从容器到HttpServlet"><span class="toc-text">从容器到HttpServlet</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Web容器做了什么"><span class="toc-text">Web容器做了什么</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#关于HttpServletRequest"><span class="toc-text">关于HttpServletRequest</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#处理请求参数与标头"><span class="toc-text">处理请求参数与标头</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#取得请求参数"><span class="toc-text">取得请求参数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#获取HTTP的标头（Header）信息"><span class="toc-text">获取HTTP的标头（Header）信息</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#请求参数编码处理"><span class="toc-text">请求参数编码处理</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#POST请求参数编码处理"><span class="toc-text">POST请求参数编码处理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#GET请求参数编码处理"><span class="toc-text">GET请求参数编码处理</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#getReader-getInputStream-读取Body内容"><span class="toc-text">getReader(), getInputStream()读取Body内容</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#getPart-getParts-取得上传文件"><span class="toc-text">getPart(), getParts() 取得上传文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用RequestDispatcher调派请求"><span class="toc-text">使用RequestDispatcher调派请求</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#使用include-方法"><span class="toc-text">使用include()方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#请求范围属性"><span class="toc-text">请求范围属性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#forward-）方法"><span class="toc-text">forward(）方法</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#关于HttpServletResponse"><span class="toc-text">关于HttpServletResponse</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#设置响应标头，缓冲区"><span class="toc-text">设置响应标头，缓冲区</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#试用getWriter-输出字符"><span class="toc-text">试用getWriter()输出字符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用getOutputStream-输出二进制字符"><span class="toc-text">使用getOutputStream()输出二进制字符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用sendRedirect-sendError"><span class="toc-text">使用sendRedirect(), sendError()</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#综合练习"><span class="toc-text">综合练习</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#微博应用程序"><span class="toc-text">微博应用程序</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#复习"><span class="toc-text">复习</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#会话管理"><span class="toc-text">会话管理</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#会话基本原理"><span class="toc-text">会话基本原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用隐藏域"><span class="toc-text">使用隐藏域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用Cookie"><span class="toc-text">使用Cookie</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用URL重写"><span class="toc-text">使用URL重写</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HttpSession会话管理"><span class="toc-text">HttpSession会话管理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用HttpSession"><span class="toc-text">使用HttpSession</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpSession会话管理原理"><span class="toc-text">HttpSession会话管理原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpSession与URL重写"><span class="toc-text">HttpSession与URL重写</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#综合练习-1"><span class="toc-text">综合练习</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#修改微博应用程序"><span class="toc-text">修改微博应用程序</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Servlet进阶API-过滤器与监听器"><span class="toc-text">Servlet进阶API,过滤器与监听器</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Servlet进阶API"><span class="toc-text">Servlet进阶API</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Servlet-ServletConfig-与-GenericServlet"><span class="toc-text">Servlet, ServletConfig 与 GenericServlet</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用ServletConfig"><span class="toc-text">使用ServletConfig</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ServletContext"><span class="toc-text">ServletContext</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#应用程序事件-监听器"><span class="toc-text">应用程序事件,监听器</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#ServletContext事件，监听器"><span class="toc-text">ServletContext事件，监听器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpSession事件，监听器"><span class="toc-text">HttpSession事件，监听器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HttpServletRequest事件，监听器"><span class="toc-text">HttpServletRequest事件，监听器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#过滤器"><span class="toc-text">过滤器</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#实现与设置过滤器"><span class="toc-text">实现与设置过滤器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#请求封装器"><span class="toc-text">请求封装器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#响应过滤器"><span class="toc-text">响应过滤器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#从JSP到Servlet"><span class="toc-text">从JSP到Servlet</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#JSP生命周期"><span class="toc-text">JSP生命周期</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Servlet至JSP的简单转换"><span class="toc-text">Servlet至JSP的简单转换</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#指示元素"><span class="toc-text">指示元素</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#声明，Scriptlet与表达式元素"><span class="toc-text">声明，Scriptlet与表达式元素</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#注释元素"><span class="toc-text">注释元素</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#隐式对象-隐式变量"><span class="toc-text">隐式对象/隐式变量</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#错误处理"><span class="toc-text">错误处理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#标准标签-Standard-Tag"><span class="toc-text">标准标签(Standard Tag)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#lt-jsp-include-gt-lt-jsp-forward-gt-标签"><span class="toc-text"><jsp:include>, <jsp:forward>标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt-简介"><span class="toc-text"><jsp:useBean>, <jsp:setProperty>与<jsp:getProperty>简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#深入-lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt"><span class="toc-text">深入<jsp:useBean>, <jsp:setProperty>与<jsp:getProperty></span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#谈谈Model1"><span class="toc-text">谈谈Model1</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#XML格式标签"><span class="toc-text">XML格式标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#表达式语言-EL"><span class="toc-text">表达式语言(EL)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#EL简介"><span class="toc-text">EL简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用EL取得属性"><span class="toc-text">使用EL取得属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#缺页"><span class="toc-text">缺页</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#EL运算符"><span class="toc-text">EL运算符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#复习-1"><span class="toc-text">复习</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#使用JSTL"><span class="toc-text">使用JSTL</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#核心标签库"><span class="toc-text">核心标签库</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#流程处理标签"><span class="toc-text">流程处理标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#错误处理标签"><span class="toc-text">错误处理标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#网页导入，重定向，URL处理标签"><span class="toc-text">网页导入，重定向，URL处理标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#属性处理与输出标签"><span class="toc-text">属性处理与输出标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#I18N兼容格式标签库"><span class="toc-text">I18N兼容格式标签库</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#I18N基础"><span class="toc-text">I18N基础</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#信息标签"><span class="toc-text">信息标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#地区标签"><span class="toc-text">地区标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#格式标签"><span class="toc-text">格式标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#XML标签库"><span class="toc-text">XML标签库</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#函数标签库"><span class="toc-text">函数标签库</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#自定义标签"><span class="toc-text">自定义标签</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#整合数据库"><span class="toc-text">整合数据库</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#JDBC入门"><span class="toc-text">JDBC入门</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#JDBC简介"><span class="toc-text">JDBC简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用Statement，ResultSet"><span class="toc-text">使用Statement，ResultSet</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用PreparedStatement-CallableStatement"><span class="toc-text">使用PreparedStatement,CallableStatement</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#JDBC进阶"><span class="toc-text">JDBC进阶</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用DataSource取得连接"><span class="toc-text">使用DataSource取得连接</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用ResultSet卷动，跟新数据"><span class="toc-text">使用ResultSet卷动，跟新数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#批次更新"><span class="toc-text">批次更新</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Blob与Clob"><span class="toc-text">Blob与Clob</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#事务简介"><span class="toc-text">事务简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#metadata简介"><span class="toc-text">metadata简介</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#RowSet简介"><span class="toc-text">RowSet简介</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用SQL标签库"><span class="toc-text">使用SQL标签库</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#数据源，查询标签"><span class="toc-text">数据源，查询标签</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#更新，参数，事务标签"><span class="toc-text">更新，参数，事务标签</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Web容器安全管理"><span class="toc-text">Web容器安全管理</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#JavaMail入门"><span class="toc-text">JavaMail入门</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#从模式到框架"><span class="toc-text">从模式到框架</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#认识设计模式"><span class="toc-text">认识设计模式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Template-Method模式-Gof设计模式"><span class="toc-text">Template Method模式(Gof设计模式)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Intercepting-Filter模式-Java-EE-设计模式"><span class="toc-text">Intercepting Filter模式(Java EE 设计模式)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Model-View-Controller模式-架构模式"><span class="toc-text">Model-View-Controller模式(架构模式)</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#重构，模式与框架"><span class="toc-text">重构，模式与框架</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Business-Delegate-委托-模式"><span class="toc-text">Business Delegate(委托)模式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Service-Locator-定位器-模式"><span class="toc-text">Service Locator(定位器)模式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Transfer-转移-Object模式"><span class="toc-text">Transfer(转移) Object模式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Front-Controller模式"><span class="toc-text">Front Controller模式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#库与框架"><span class="toc-text">库与框架</span></a></li></ol></li></ol></li></ol>
</div>

            <!-- Post Main Content -->
            <div class="post-content col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <h1 id="Web应用程序简介"><a href="#Web应用程序简介" class="headerlink" title="Web应用程序简介"></a>Web应用程序简介</h1><h2 id="URL"><a href="#URL" class="headerlink" title="URL"></a>URL</h2><ul>
<li><p>URL(Uniform Resource Locator)的主要格式为：&lt;协议&gt;:&lt;特定协议部分&gt;</p>
<ul>
<li>协议(scheme)指定了以何种方式取得资源。<ul>
<li>ftp(文件传输协议，File Transfer Protocol)</li>
<li>http(超文本传输协议，HyperText Transfer Protocol)</li>
<li>mailto(电子邮件)</li>
<li>file(特定主机文件名)</li>
</ul>
</li>
<li>协议之后跟随冒号，特定协议部分的格式则为：//&lt;用户&gt;:&lt;密码&gt;@&lt;主机&gt;:&lt;端口号&gt;/&lt;路径&gt;<ul>
<li><a href="http://openhome.cc:8080/Gossip/index.html" target="_blank" rel="external">http://openhome.cc:8080/Gossip/index.html</a></li>
<li>file://c:/workspace/jdbc.pdf</li>
</ul>
</li>
</ul>
</li>
<li><p>URL代表资源的地址信息，URN(Uniform Resource Name)则代表某个资源独一无二的名称。如：国家标准书号(ISBN)</p>
</li>
<li><p>由于URL或URN的目的，都是用啦标识某个资源，后来的标准制定了URI，而URL与URN成为URI的子集。</p>
</li>
</ul>
<h2 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h2><ul>
<li><p>HTTP两个基本但极为重要的特性：</p>
<ul>
<li><p>基于请求(Request)/响应(Response)模型</p>
<ul>
<li>客户端对服务器发出一个取得资源的请求，服务器将要求的资源响应个客户端</li>
<li>每次联机只作一次请求/响应</li>
<li>没有请求就不会有响应</li>
</ul>
</li>
<li><p>无状态(Stateless)通信协议</p>
<ul>
<li>服务器响应客户端之后，就不会记得客户端的信息</li>
</ul>
</li>
</ul>
</li>
<li><p>浏览器在使用HTTP发出请求时，可以有几种请求方法，如GET, POST, HEAD, PUT, DELETE等。</p>
</li>
</ul>
<h1 id="编写与设置servlet"><a href="#编写与设置servlet" class="headerlink" title="编写与设置servlet"></a>编写与设置servlet</h1><h2 id="在HelloServlet之后"><a href="#在HelloServlet之后" class="headerlink" title="在HelloServlet之后"></a>在HelloServlet之后</h2><h3 id="使用-WebServlet"><a href="#使用-WebServlet" class="headerlink" title="使用@WebServlet"></a>使用@WebServlet</h3><pre><code class="Java">@WebServlet {
    name=&quot;Hello&quot;,
    urlPatterns={&quot;/hello.view&quot;},
    loadOnStartup=1
}
</code></pre>
<h3 id="使用Web-xml"><a href="#使用Web-xml" class="headerlink" title="使用Web.xml"></a>使用Web.xml</h3><pre><code class="xml">&lt;servlet&gt;
    &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;cc.openhome.HelloServlet&lt;/servlet-class&gt;
    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
    &lt;servlet-name&gt;HelloServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/helloUser.view&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</code></pre>
<h3 id="文件组织与部署"><a href="#文件组织与部署" class="headerlink" title="文件组织与部署"></a>文件组织与部署</h3><ul>
<li>WEB-INF: 这个目录是固定的，而且一定是位于应用程序根目录下。放置在WEB-INF中的文件或目录，对外界来说是封闭的，也就是客户端无法试用HTTP的任何方式直接访问到WEB-INF中的文件或目录。若有这类需要，则必须通过Servlet/Jsp的请求转发(Forward)。不想让外界存取的资源，可以放置在这个目录下。</li>
</ul>
<h2 id="进阶部署设置"><a href="#进阶部署设置" class="headerlink" title="进阶部署设置"></a>进阶部署设置</h2><h3 id="URL模式设置"><a href="#URL模式设置" class="headerlink" title="URL模式设置"></a>URL模式设置</h3><ul>
<li><p>一个请求URI实际上是由三个部分组成的：</p>
<blockquote>
<p>requestURI = contentPath + servletPath + pathInfo<br>   getRequestURI()</p>
</blockquote>
<ul>
<li><p>环境变量（Context path) - getContentPath()</p>
<ul>
<li>路径映射(Path mapping): 以“/”开头但以“/*”结尾的URL模式。</li>
<li>扩展映射(extension mapping): 以“*.“开头的URL模式。</li>
<li>环境根目录(Context root): 空字符串”“是个特殊的URL模式，对应至环境根目录，也就是”/“的请求，但不用与设置<url-pattern>或urlPattern属性。</url-pattern></li>
<li>预设Servlet: 仅包括”/“的URL模式对应时，就会使用预设Servlet。</li>
<li>完全匹配(Exact match): 不符合以上设置的其他字符串，都要作路径的严格对应。</li>
</ul>
</li>
<li><p>Servlet路径 - getServletPath()</p>
<ul>
<li>不包括路径信息(Path info)与请求参数(Request parameter)。</li>
</ul>
</li>
<li><p>路径信息</p>
<ul>
<li>不包括请求参数。</li>
<li>如果没有额外路径信息，则为null(扩展映射，预设Servlet，完全匹配的情况下，getPathInfo()就会取得null)</li>
<li>如果有额外信息，则是一个以”/”开头的字符串。</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>/FirstServlet/servlet/path.view = /FirstServlet + /servlet + /path.view</p>
<h3 id="Web目录结构"><a href="#Web目录结构" class="headerlink" title="Web目录结构"></a>Web目录结构</h3><ul>
<li><p>如果Web应用程序的URI最后是以“/”结尾，而且确实存在改目录，则Web容器必须传回该目录下的欢迎页面，可以在部署描述文件web.xml中包括以下的定义，指出可用的欢迎页面名称为何。</p>
<pre><code class="xml">  // Web容器会依序看看是否有对应的文件存在
  &lt;welcome-file-list&gt;
      &lt;welcome-file&gt;index.html&lt;/welcome-file&gt;
      &lt;welcome-file&gt;default.jsp&lt;/welcome-file&gt;
  &lt;/welcome-file-list&gt;

  // 如果找不到以上文件，则会尝试至JAR的/META-INF/resources中寻找已放置的资源页面。
  // 如果URL最后是以“/”结尾，但不存在该目录，则会使用，预设Servlet(如果有定义的话)
</code></pre>
</li>
</ul>
<h3 id="使用web-fragment-xml"><a href="#使用web-fragment-xml" class="headerlink" title="使用web-fragment.xml"></a>使用web-fragment.xml</h3><ol>
<li><p>web-fragment.xml</p>
<ul>
<li>一个JAR文件中，除了可使用标注定义的Servlet，监听器，过滤器，还可以拥有自己的部署描述文件，这个文件的名称是web-fragment.xml，必须放置在JAR文件的META-INF目录中。</li>
<li>web-fragment.xml的根标签是<web-fragment>而不是<web-app></web-app></web-fragment></li>
<li>实际上，web-fragment.xml中所指定的类，不一定要在JAR文件中，也可以是子啊web应用程序的/WEB-INF/classes中。</li>
</ul>
</li>
<li><p>web.xml与web-fragment.xml</p>
<ul>
<li><p>Servlet3.0对web.xml与标注的配置顺序并没有定义，对web-fragment.xml及标注的配置顺序也没有定义，然而可以决定web.xml与web-fragment.xml的配置顺序，其中一个设置方式实在web.xml中使用<absolute-ordering>定义绝对顺序。</absolute-ordering></p>
</li>
<li><p>另一个定义顺序的方式，是直接在每个JAR文件的web-fragment.xml中使用<ordering>，其中使用<before>或<after>来定义顺序。</after></before></ordering></p>
</li>
</ul>
</li>
<li><p>metadata-complete属性</p>
<ul>
<li>如果将web.xml中<web-app>的metadata-complete属性设置为true(默认是false)，则表示web.xml中已完成Web应用程序的相关定义，部署时将不会扫描标注与web-fragment.xml中的定义，如果有<absolute-ordering>与<ordering>也会被忽略。</ordering></absolute-ordering></web-app></li>
</ul>
</li>
</ol>
<h1 id="请求与响应"><a href="#请求与响应" class="headerlink" title="请求与响应"></a>请求与响应</h1><h2 id="从容器到HttpServlet"><a href="#从容器到HttpServlet" class="headerlink" title="从容器到HttpServlet"></a>从容器到HttpServlet</h2><ul>
<li>Web容器是生成Servlet/Jsp唯一认识的HTTP服务器！</li>
</ul>
<h3 id="Web容器做了什么"><a href="#Web容器做了什么" class="headerlink" title="Web容器做了什么"></a>Web容器做了什么</h3><ul>
<li><p>Web容器做的事就是，创建Servlet实例，并完成Servlet名称注册及URL模式的对应。在请求到来时，Web容器会转发给正确的Servlet来处理请求。</p>
</li>
<li><p>容器收集相关信息，并创建代表请求与响应的对象。（HttpServletRequest,HttpServletResponse接口）</p>
</li>
<li><p>Web容器本身就是一个Java所编写的应用程序。</p>
</li>
<li><p>Http服务器对浏览器进行响应，之后容器将HttpServletResponse对象，HttpServletResponse对象销毁回收，该次请求响应结束。</p>
</li>
<li><p>Web容器</p>
<ul>
<li>请求信息的收集</li>
<li>HttpServiceRequest对象，HttpServiceResponse对象等的创建</li>
<li>输出Http响应的转换</li>
<li>HttpServiceRequest对象，HttpServiceResponse对象等的销毁创建和回收。</li>
</ul>
</li>
<li><p>Servlet接口的Service方法签名(Signature)其实接受的是ServletRequest，ServletResponse:</p>
<pre><code class="Java">  public void service(ServiceRequest req, ServletResponse res) throws ServletException, IOException;
  // 当初定义Servlet时，期待的是Servlet不仅使用于HTTP。(ServletRequest, ServletResponse)-javax.servlet
  //而与HTTP相关的行为(HttpServletRequest, HttpServletResponse)-javax.servlet.http
</code></pre>
</li>
<li><p>HttpServlet中的实现service()</p>
<pre><code class="Java">  public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
      HttpServletRequest request;
      HttpServletResponse response;

      try {
          request = (HttpServletRequest) req;
          response = (HttpServletResponse) res;
      } catch(ClassCastException e) {
          throw new ServletException(&quot;non-HTTP request or response&quot;);
      }
      service(request, response);
  }

  //Http新定义的方法
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      String method = req.getMethod();
      if(method.equals(METHOD_GET)) {
          long lastModified = getLastModified(req);
          if(lastModified = -1) {
              doGet(req, resp);
          } else {
              long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
              if(ifModifiedSince &lt; (lastModified / 1000 * 1000)) {
                  maybeSetLastModified(resp, lastModified);
                  doGet(req, resp);
              } else {
                  resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
              }
          } else if(method.equals(METHOD_HEAD)) {
              long lastModified = getLastModified(req);
              maybeSetLastModified(resp, lastModified);
              doHead(req, resp);
          } else if(method.equals(METHOD_POST) {
              doPost(req, resp);
          } else if(method.equals(METHOD_PUT)) {
              ...
          }
      }
</code></pre>
</li>
<li><p>继承HttpServlet之后，没有重新定义doGet()方法，而客户端对该Servlet发出了GET请求，则会收到错误信息。</p>
</li>
<li>对于GET请求，可以实现getLastModified()方法（默认返回-1，也就是默认不支持if-modified-since表头），来决定是否调用doGet()方法，getLastModified()方法返回自1970年1月1日凌晨至资源最后一次更新期间所经过的毫秒数，返回的这个时间如果晚于浏览器发出的if-modified-since标头，才会调用doGet()方法</li>
</ul>
<h2 id="关于HttpServletRequest"><a href="#关于HttpServletRequest" class="headerlink" title="关于HttpServletRequest"></a>关于HttpServletRequest</h2><h3 id="处理请求参数与标头"><a href="#处理请求参数与标头" class="headerlink" title="处理请求参数与标头"></a>处理请求参数与标头</h3><h4 id="取得请求参数"><a href="#取得请求参数" class="headerlink" title="取得请求参数"></a>取得请求参数</h4><pre><code class="Java">    String getParameter();

    String username = request.getParameter(&quot;name&quot;);
    // 若请求中没有所指定的参数名称，则返回null

    String[] getParameterValues();
    Enumeration getParameterNames();

    Enumeration&lt;String&gt; e = req.getParameterNames();

    Map getParameterMap();
    // Map中的键是请求参数名称，值的部分是请求参数值，以字符串数组类型String[]返回（考虑有同一请求参数有多个值的情况）
</code></pre>
<h4 id="获取HTTP的标头（Header）信息"><a href="#获取HTTP的标头（Header）信息" class="headerlink" title="获取HTTP的标头（Header）信息"></a>获取HTTP的标头（Header）信息</h4><pre><code class="Java">    String getHeader();//指定标头名称后可返回字符串值，代表浏览器所送出的标头信息
    Enumeration getHeaders();//元素为字符串
    Enumeration getHeaderNames();//取得标头名称

    @Webservlet(&quot;/header.view&quot;)
    public class HeaderServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest res, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter out = resp.getWriter();
            out.println(&quot;&lt;html&gt;&quot;);
            out.println(&quot;&lt;head&gt;&quot;);
            out.println(&quot;&lt;title&gt;HeaderServlet&lt;/title&gt;&quot;);
            out.println(&quot;&lt;/head&gt;&quot;);
            out.println(&quot;&lt;body&gt;&quot;);
            out.println(&quot;&lt;h1&gt;HeaderServlet at&quot; + req.getContextPath() + &quot;&lt;/h1&gt;&quot;);
            Enumeration&lt;String&gt; names = req.getHeaderNames();
            while(names.hasMoreElements() {
                String name = names.nextElements();
                out.println(name + &quot;:&quot; + req.getHeader(name) + &quot;&lt;br&gt;&quot;);
            }
            out.println(&quot;&lt;/body&gt;&quot;);
            out.println(&quot;&lt;/html&gt;&quot;);
        }
    }

标头值本身是个整数或日期的字符串表示法
getIntHeader() 
// 转换为int 失败则 抛出NumberFormatException
getDateHeader()
// 转换为Date 失败则抛出IllegalArgumentException
</code></pre>
<h3 id="请求参数编码处理"><a href="#请求参数编码处理" class="headerlink" title="请求参数编码处理"></a>请求参数编码处理</h3><h4 id="POST请求参数编码处理"><a href="#POST请求参数编码处理" class="headerlink" title="POST请求参数编码处理"></a>POST请求参数编码处理</h4><pre><code class="Java">// 网页编码是UTF-8，相当于浏览器做了这个操作
String text = java.net.URLEncoder.encode(&quot;林&quot;， “UTF-8；“)

// 在Servlet中取得请求参数时，容器若默认使用ISO-8859-1来处理编码
String text = java.net.URLDecoder.decode(&quot;%E6%9E%97&quot;, &quot;ISO-8859-1&quot;);

req.setCharacterEncoding(&quot;UTF-8&quot;);
// 指定取得POST请求参数时使用的编码
</code></pre>
<h4 id="GET请求参数编码处理"><a href="#GET请求参数编码处理" class="headerlink" title="GET请求参数编码处理"></a>GET请求参数编码处理</h4><ul>
<li><p>setCharacterEncoding()这个对于请求Body中的字符编码才有作用，也就是只对POST产生作用。</p>
</li>
<li><p>通过String的getBytes()指定编码来取得该字符串的字节数组，然后重新构造为正确编码的字符串</p>
<pre><code class="Java">  String name = req.getParameter(&quot;name&quot;);
  String name = new String(name.getBytes(&quot;ISO-8859-1&quot;), &quot;UTF-8&quot;);
</code></pre>
</li>
</ul>
<h3 id="getReader-getInputStream-读取Body内容"><a href="#getReader-getInputStream-读取Body内容" class="headerlink" title="getReader(), getInputStream()读取Body内容"></a>getReader(), getInputStream()读取Body内容</h3><ul>
<li>form标签没有设置enctype属性，默认值是application/x-www-form-urlencoded</li>
<li>如果上传文件，enctype属性要设为multipart/form-data</li>
<li><p>同一个请求期间，getReader()和getInputStream()只能择一调用，否则，抛出illegalStateException异常</p>
</li>
<li><p>例子：Servlet处理上传文件</p>
</li>
</ul>
<h3 id="getPart-getParts-取得上传文件"><a href="#getPart-getParts-取得上传文件" class="headerlink" title="getPart(), getParts() 取得上传文件"></a>getPart(), getParts() 取得上传文件</h3><ul>
<li><p>Tomcat中必须设置标注（@MultioartConfig）才能使用getPart()相关API</p>
</li>
<li><p>@MultipartConfig 标注可用来设置Servlet处理上传的相关信息</p>
<ul>
<li>fileSizeThreshold 整数值设置，若上传文件大小超过设置门槛，会先写入缓存文件，默认值为0</li>
<li>location 字符串设置，设置写入文件时的目录</li>
<li>maxFileSize 限制上传文件大小，默认值为-1L，表示不限制大小</li>
<li><p>maxRequestSize 限制multipart/form-data 请求个数，默认值为-1L，表示不限个数。</p>
<pre><code class="Java">@MultipartConfig(location=&quot;c:/workspace&quot;)
@WebServlet(&quot;/upload2.do&quot;)
public class UploadServlet2 extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      req.setCharacterEncoding(&quot;UTF-8&quot;);//上传的文件名可能会有中文
      Part part = req.getPart(&quot;photo&quot;);
      String filename = getFilename(part);
      part.write(filename);
  }

  private String getFilename(Part part) {
      String header = part.getHeader(&quot;Content-Disposition&quot;);
      String filename = header.substring(header.indexOf(&quot;filename=\&quot;&quot;) + 10, header.lastIndexOf(&quot;\&quot;&quot;));
      return filename;
  }
}
</code></pre>
</li>
</ul>
<ul>
<li><p>getParts()方法，返回一个Collection<part>,其中每个上传文件的Part对象。</part></p>
<pre><code class="Java">for(Part part : req.getParts()) {
    if(part.getName.startsWith(&quot;file&quot;)) {
        String filename = getFilename(part)；
        part.write(filename);
    }
    //if的原因，因为”上传“按钮也会是其中一个Part对象。 
}
</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="使用RequestDispatcher调派请求"><a href="#使用RequestDispatcher调派请求" class="headerlink" title="使用RequestDispatcher调派请求"></a>使用RequestDispatcher调派请求</h3><ul>
<li><p>在Web应用程序中，经常需要多个Servlet来完成请求。例如，将另一个Servlet的请求处理流程包含（Include）进来，或将请求转发（Forward）给别的Servlet处理。</p>
</li>
<li><p>使用HttpServletRequest的getRequestDispatcher()方法取得RequestDispatcher接口的实现对象实例，调用时指定转发或包含的相对URL网址</p>
<pre><code class="Java">  RequestDispatcher dispatcher = request.getRequstDispatcher(&quot;some.do&quot;);
  // 取得RequestDispatcher还有两个方式，通过ServletContext的getRequestDispatcher()或getNameDispatcher()
</code></pre>
</li>
</ul>
<h4 id="使用include-方法"><a href="#使用include-方法" class="headerlink" title="使用include()方法"></a>使用include()方法</h4><ul>
<li><p>将另一个Servlet的操作流程包括至目前Servlet操作流程之中。</p>
<pre><code class="Java">  PrintWriter out = resp.getWriter();
  out.println(&quot;Some do one...&quot;);
  RequestDispatcher dispatcher = req.getRequestDispatcher(&quot;other.view&quot;);
  dispatcher.include(req, resp);
  out.println(&quot;Some do two...&quot;);
  out.close();
  // other.view 实际上会依URL模式取得对应的Servlet

  // 在取得RequestDispatcher时，也可以包括查询字符串
  req.getRequestDispatcher(&quot;other.view?data=123456&quot;).include(req, resp);
  // 那么在被包含（或转发，如果使用的是forward())的Servlet中就可以使用getParameter(&quot;data&quot;)取得请求参数值。
</code></pre>
</li>
</ul>
<h4 id="请求范围属性"><a href="#请求范围属性" class="headerlink" title="请求范围属性"></a>请求范围属性</h4><ul>
<li><p>在include()或forward()时包括参数的做法，仅适用于传递字符串值另一个Servlet。</p>
</li>
<li><p>在调派请求的过程中，如果有必须共享的”对象“，可以设置给请求对象成为属性，称为请求范围属性(Request Scope Arrribute)。</p>
<ul>
<li>setAttribute: 指定名称与对象设置属性。</li>
<li>getAttribute: 指定名称取得属性</li>
<li>getAttributeNames(): 取得所有属性名称。</li>
<li><p>removeAttribute(): 指定名称移除属性。</p>
<pre><code class="Java">List&lt;Book&gt; books = bookDAO.query(&quot;ServletJSP&quot;);
request.setAttribute(&quot;books&quot;, books);
request.getRequestDispatcher(&quot;result.view&quot;).include(request, response);

List&lt;Book&gt; books = (List&lt;Book&gt;) request.getAttribute(&quot;books&quot;);
</code></pre>
</li>
</ul>
</li>
<li><p>保留属性，表示上一个Servlet，如果被包含的Servlet还包括其他的Servlet，则这些属性名称的对应值也会被代换。</p>
<ul>
<li>javax.servlet.include.request_url</li>
<li>javax.servlet.include.context_path</li>
<li>javax.servlet.include.servlet_path</li>
<li>javax.servlet.include.path_info</li>
<li>javax.servlet.include.query_string</li>
</ul>
</li>
</ul>
<h4 id="forward-）方法"><a href="#forward-）方法" class="headerlink" title="forward(）方法"></a>forward(）方法</h4><ul>
<li><p>调用时同样传入请求与响应对象，这表示你要将请求处理转发给别的Servlet，”对客户端响应同时也转发给另一个Servlet“</p>
<blockquote>
<p>若要调用forward()方法，目前Servlet不能有任何响应确认(Commit)，如果在目前的Servlet中通过响应对象设置了一些响应但未确认，则所有响应设置会别忽略。如果已经有响应确认且调用了forward()方法，则会抛出IllegalStateException。</p>
</blockquote>
</li>
</ul>
<pre><code class="Servlet">@WebServlet(&quot;/hello.do&quot;);
public class HelloController extends HttpServlet {
    private HellModel model = new 
    HelloModel();
    @override
    protected void doGet(HttpServletRequest request, HttpServletException response) throws ServletException, IOException {
        String name = request.getParameter(&quot;user&quot;);
        String message = model.doHello(name);
        request.setAttribute(&quot;message&quot;, message);
        request.getRequestDispatcher(&quot;hello.view&quot;).forward(request, response);
    }
}

......

public class HelloModel {
    private Map&lt;String, String&gt; messages = new HashMap&lt;String, String&gt;();
    public HelloModel() {
        messages.put(&quot;caterpillar&quot;, &quot;Hello&quot;);
        messages.put(&quot;Justin&quot;, &quot;Welcome&quot;);
        messages.put(&quot;momor&quot;, &quot;Hi&quot;);
    } 

    public String doHello(String user) {
        String message = messages.get(user);
        return message + &quot;, &quot; + user + &quot;!&quot;;
    }
}
</code></pre>
<h2 id="关于HttpServletResponse"><a href="#关于HttpServletResponse" class="headerlink" title="关于HttpServletResponse"></a>关于HttpServletResponse</h2><h3 id="设置响应标头，缓冲区"><a href="#设置响应标头，缓冲区" class="headerlink" title="设置响应标头，缓冲区"></a>设置响应标头，缓冲区</h3><ul>
<li><p>设置响应标头：setHeader()设置标头名称与值，addHeader()则可以在同一个标头名称上附加值。</p>
<blockquote>
<p>如果标头的值是整数，则可以试用setIntHeader(),addIntHeader()方法<br>  如果标头的值是个日期，则可以使用setDateHeader(),addDateHeader()方法</p>
</blockquote>
</li>
<li><p>所有的标头设置，必须在响应确认之前（Commit），在响应确认之后设置的标头，会被容器忽略。</p>
</li>
<li><p>容器可以对响应进行缓冲，通常容器默认都会响应进行缓冲</p>
<pre><code class="Java">  getBufferSize()
  setBufferSize()  // 必须在调用HttpServletResponse的getWriter()或getOutputStream()方法之前调用，所取得的 Writer或ServletOutputStream才会套用这个设置。之后调用，会抛出IllegalStateException

  isCommitted()  // 在缓冲区未满之前，设置的响应相关内容都不会真正传至客户端，可以试用isCommitted()看看是否响应确认

  reset()  // 重置所有响应内容，这回连同已设置的标头一并清除
  resetBuffer()  // 重置响应内容，但不会清除已设置的标头内容
  // reset（），resetBuffer（）必须在响应未确认前调用，否则，抛出IllegalStateException

  flushBuffer()  // 会清除所有缓冲区中已已设置的响应信息至可以端
</code></pre>
</li>
<li><p>HttpServletResponse对象若被容器关闭，则必须清除所有的响应内容，响应对象被关闭的时机有<br>以下几种：</p>
<ul>
<li>Servlet的service()方法已结束，响应的内容长度超过HttpServletResponse的setContentLength()所设置的长度</li>
<li>调用了sendRedirect()方法</li>
<li>调用了sendError()方法</li>
<li>调用了AsyncContext的complete方法</li>
</ul>
</li>
</ul>
<h3 id="试用getWriter-输出字符"><a href="#试用getWriter-输出字符" class="headerlink" title="试用getWriter()输出字符"></a>试用getWriter()输出字符</h3><ul>
<li><p>在没用设置任何内容类型或编码之前，HttpServletResponse使用的字符编码默认是ISO-8859-1</p>
</li>
<li><p>影响HttpServletResponse输出的编码处理方式</p>
<ul>
<li><p>设置Locale</p>
<ul>
<li>请求中有Accept-Language标头，可以使用getLocale()来取得一个Locale对象，代表客户端可接受的语系。</li>
<li><p>响应，setLocale()来设置地区（Locale）信息，地区信息就包括了语系与编码信息。</p>
<ul>
<li><p>语系信息通常通过响应标头Content-Language来设置</p>
<p>```Java<br>resp.setLocale(Locale.TAIWAN);<br>// 将HTTP响应的Content-Language设置为zh-TW，而字符编码处理为BIG5</p>
<p>// HttpServletResponse的getCharacterEncoding()方法取得编码设置。<br>// 在web.xml中设置默认的区域与编码对应</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>        &lt;locale-encoding-mapping-list&gt;
            &lt;locale-encoding-mapping&gt;
                &lt;locale&gt;zh_TW&lt;locale&gt;
                &lt;encoding&gt;UTF-8&lt;encoding&gt;
            &lt;/locale-encoding-mapping&gt;
        &lt;/locale-encoding-mapping-list&gt;

        resp.setLocale(Locale.TAIWAN);
        // 或者是
        resp.setLocale((new Locale(&quot;zh&quot;, &quot;TW&quot;);
        // 字符编码处理就采用UTF-8（web.xml的设置）
        ```

+ 使用setCharacterEncoding()或setContentType()

    ```Java
    resp.setCharacterEncoding(&quot;UTF-8);

    resp.setContentType(&quot;text/html; charset=UTF-8&quot;);
    // 设置内容类型为text/html,也会自动调用setCharacterEncoding(),设置编码为UTF-8
    ```

    + 如果使用了setCharacterEncoding()或 setContentType时指定了charset，则setLocale()就会被忽略
    + 如果要接受中文请求参数并在响应时通过浏览器正确显示中文，必须同时设置HttpServletRequest的 setCharacterEncoding()以及 HttpServletResponse的 setCharacterEncoding()或 setContentType()为正确的编码
    + 浏览器需要知道如何处理你的响应，所以必须告知内容类型 setContentType()，指定MIME（Multipurpose Internet Mail Extensions）

    + charset属性，常见的设置有text/html, application/pdf, application/jar, application/x-zip, image/jpeg等

    ```xml
        &lt;mime-mapping&gt;
            &lt;extension&gt;pdf&lt;/extension&gt;
            &lt;mime-type&gt;application/pdf&lt;/mime-type&gt;
        &lt;mime-mapping&gt;
    // &lt;extension&gt;设置文件的后缀，而&lt;mime-type&gt;设置对应的MIME类型名称。
    // 使用ServletContent的getMimeType()方法
    // 指定文件名称，根据web.xml 中设置的后缀对应，取得MIME类型名称
    ```

    ```Java
    @WebServlet(&quot;/pet&quot;)
    public class Pet extends HttpServlet {
        @Override
        protected void doPost(HttpServletResponse request, HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding(&quot;UTF-8&quot;);
            response.setContentType(&quot;text/html; charset=UTF-8&quot;);

            PrintWrite out = response.getWriter();
            out.println(&quot;&lt;html&gt;&quot;);
            out.println(&quot;&lt;head&gt;&quot;);
            out.println(&quot;&lt;title&gt;感谢填写&lt;/title&gt;&quot;);
            out.println(&quot;&lt;/head&gt;&quot;);
            out.println(&quot;&lt;body&gt;&quot;);
            out.println(&quot;联系人：&lt;a href=&#39;mailto:&quot; 
            + request.getParameter(&quot;email&quot;) + &quot;&#39;&gt;&quot; 
            + request.getParameter(&quot;user&quot;) + &quot;&lt;/a&gt;&quot;);
            out.println(&quot;&lt;br&gt;喜爱的宠物类型&quot;);
            out.println(&quot;&lt;ul&gt;&quot;);
            for(String type : request.getParameterValues(&quot;type&quot;)) { // 取得多级菜单
            out.println(&quot;&lt;li&gt;&quot; + type + &quot;&lt;/li&gt;&quot;);
            }
            out.println(&quot;&lt;/ul&gt;&quot;);
            out.println(&quot;&lt;/body&gt;&quot;);
            out.println(&quot;&lt;/html&gt;&quot;);
            out.close();
        }
    }
    ```
</code></pre><h3 id="使用getOutputStream-输出二进制字符"><a href="#使用getOutputStream-输出二进制字符" class="headerlink" title="使用getOutputStream()输出二进制字符"></a>使用getOutputStream()输出二进制字符</h3><ul>
<li>大部分情况下，会从HttpServletResponse 取得PrintWriter实例，使用println() 对浏览器进行字符输出。</li>
<li>有时候，需要直接对浏览器进行字节输出，这时可以使用HttpServletResponse的getOutputStream实例。它是OutputStream的子类</li>
</ul>
<pre><code class="Java">@WebServlet(&quot;/download.do&quot;)
public class Download extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        String passwd = request.getParameter(&quot;passwd&quot;);
        if(&quot;123455&quot;.equals(passwd)) {
            response.setContentType(&quot;application/pdf&quot;);
            InputStream in = getServletContent().getResourceAsStream(&quot;/WEB-INF/jdbc.pdf&quot;);
            outputStream out = response.getOutputStream();
            WriteBytes(in, out);
        }
    }

    private void writeBytes(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[1024];
        int length = -1;
        while ((length = in.read(buffer)) != -1) {
            out.write(buffer, 0, length);
        }
        in.close();
        out.close();
    }
}

// 为了取得Web应用程序中的文件串流，可以使用HttpServlet的 getServletContext()取得ServletContext对象，这个对象代表了目前这个Web应用程序，可以使用ServletContext的getResourceAsStream()方法以串流程序读取文件，制定的路径要是相对于Web应用程序环境根目录
</code></pre>
<h3 id="使用sendRedirect-sendError"><a href="#使用sendRedirect-sendError" class="headerlink" title="使用sendRedirect(), sendError()"></a>使用sendRedirect(), sendError()</h3><ul>
<li>forward()会将请求转发至指定URL，这个动作实在Web容器中进行的，浏览器并不会知道请求被转发，地址栏也不会有变化。</li>
<li><p>在转发过程中，都还是在同一个请求周期。在HttpServletResponse中使用setAttribute()设置的属性对象，都可以在转发过程中共享。</p>
</li>
<li><p>使HttpServletResponse 的sendRedirect要求浏览器重新请求另一个URL，称为重定向。使用时可指定绝对URL或相对URL</p>
</li>
</ul>
<pre><code class="Java">response.sendRedirect(&quot;http://openhome.cc&quot;);
// 这个方法会在响应中设置HTTP状态码301以及Location标头，浏览器接收到这个标头，会重新使用GET方法请求指定的URL，因此在地址栏上会发现URL的变更。
// 由于是利用HTTP状态码与标头信息，要求浏览器重定向网页，因此这个方法必须子在响应未确认输出前执行，否则会抛出IllegalStateException

// 处理请求的过程中发现一些错误,想要传递服务器默认的状态与错误信息
// 请求参数必须返回的资源根本不存在：
response.sendError(HttpServletResponse.SC_NOT_FOUND);
// SC_NOT_FOUND会令服务器响应404状态码，这类常数定义在HttpServletResponse接口上

// 自定义文字
response.sendError(HttpServletResponse.SC_NOT_FOUND, &quot;笔记文件&quot;);
// HttpServlet的doGet()为例，其默认实现就使用了sendError()方法
// 在响应未确认输出前执行，否则会抛出IllegalStateException
</code></pre>
<h2 id="综合练习"><a href="#综合练习" class="headerlink" title="综合练习"></a>综合练习</h2><h3 id="微博应用程序"><a href="#微博应用程序" class="headerlink" title="微博应用程序"></a>微博应用程序</h3><h2 id="复习"><a href="#复习" class="headerlink" title="复习"></a>复习</h2><ul>
<li>使用forward()作请求转发，是将响应的职责转发给别的URL，所以字啊这之前不可以有实际的响应，否则会发生IllegalStateException异常</li>
</ul>
<h1 id="会话管理"><a href="#会话管理" class="headerlink" title="会话管理"></a>会话管理</h1><h2 id="会话基本原理"><a href="#会话基本原理" class="headerlink" title="会话基本原理"></a>会话基本原理</h2><ul>
<li>记得此次请求与之后请求间关系的方式，称为会话管理</li>
<li>实现会话管理的基本方式：<ul>
<li>隐藏域(Hidden Field),Cookie与URL重写(URL Rewriting)</li>
</ul>
</li>
</ul>
<h3 id="使用隐藏域"><a href="#使用隐藏域" class="headerlink" title="使用隐藏域"></a>使用隐藏域</h3><ul>
<li>服务器不会记得两次请求间的关系，那就由浏览器在每次请求时“主动告知”服务器多次请求间必要的信息，服务器只要单纯地处理请求中的相关信息即可。</li>
<li>可以用隐藏域的方式放在下一页的窗体中这样发送下一页窗体时，就可以一并发送这些隐藏域，每一页的问卷答案就可以保留下来。</li>
<li>隐藏域不是Servlet/Jsp实际管理会话时的机制。</li>
</ul>
<h3 id="使用Cookie"><a href="#使用Cookie" class="headerlink" title="使用Cookie"></a>使用Cookie</h3><ul>
<li><p>Web 应用程序会话管理的基本方式，就是在此次请求中，将下一次请求时服务器应知道的信息，先响应给浏览器，由浏览器在之后的请求再一并发送给应用程序，这样应用程序就可以“得知”多次请求的相关数据。</p>
</li>
<li><p>Cookie 是在浏览器存储信息的一种方式，服务器可以响应浏览器 set-cookie标头，浏览器收到这个标头与数值后，会将它以文件的形式存储在计算机上。</p>
</li>
<li><p>可以设定给 Cookie 一个存活期限，保留一些有用的信息在客户端。</p>
</li>
<li><p>如果关闭浏览器之后，再次打开浏览器并连接服务器时，这些 cookie 仍在有效期限中，浏览器会使用 cookie 标头自动将 Cookie 发送给服务器，服务器就可以得知一些先前浏览器请求的相关信息。</p>
</li>
<li><p>Cookie 可以设定存活期限，所以在客户端存储的信息可以活得更久一些（除非用户主动清除 Cookie 信息）。</p>
</li>
</ul>
<pre><code class="Java">Cookie cookie = new Cookie(&quot;user&quot;, &quot;caterpillar&quot;);
cookie.setMaxAge(7 * 24 * 60 * 60);// 单位是“秒”;
response.addCookie(cookie);
</code></pre>
<ul>
<li>HTTP中 Cookie 的设定是通过 set-cookie 标头，所以必须在实际响应浏览器之前使用addCookie() 来新增 Cookie 实例。</li>
<li>Cookie 有效期限，默认关闭浏览器之后就失效。</li>
</ul>
<pre><code class="Java">HTTPServletResponse getCookies();// 取得属于该网页所属域（Domain）的所有Cookie，返回值是Cookie[]数组。
Cookie[] cookies = request.getCookies();
if(cookies != null) {
    for(Cookie cookie : cookies) {
        String name = cookie.getName();
        String value = cookie.getValue();
        ...
    }
}
</code></pre>
<ul>
<li>Cookie另一个常见的应用，就是实现用户自动登录（Login）功能。</li>
</ul>
<pre><code class="Java">@WebServlet(&quot;/index.do&quot;)
public class Index extends HttpServlet {
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie[] cookies = request.getCookies();
        if(cookies != null) {
            for(Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();
                if(&quot;user&quot;.equals(name) &amp;&amp; &quot;caterpillar&quot;.equals(value)) {
                    request.setAttribute(name, value);
                    request.getRequestDispatcher(&quot;/user.view&quot;).forward(request, response);
                    return ;
                }
            }
        }
        response.sendRedirect(&quot;login.html&quot;);// 重定向到登录窗体
    }
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    processRequest(request, response);
}
</code></pre>
<ul>
<li>自动登录</li>
</ul>
<pre><code class="Java">@WebServlet(&quot;/login.do&quot;)
public class Login extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String user = request.getParameter(&quot;user&quot;);
        String passwd = request.getParameter(&quot;passwd&quot;);
        if(&quot;caterpillar&quot;.equals(user) &amp;&amp; &quot;123456&quot;.equals(passwd)) {
            String login = request.getParameter(&quot;login&quot;);
            if(&quot;auto&quot;.equals(login)) {
                Cookie cookie = new Cookie(&quot;user&quot;, &quot;caterpillar&quot;);
                cookie.setMaxAge(7 * 24 * 60 * 60);
                response.addCookie(cookie);
            }
            request.setAttribute(&quot;user&quot;, user);
            request.getRequestDispatcher(&quot;user.view&quot;).forward(request, response);
        }else {
            response.sendRedirect(&quot;login.html&quot;);
        }
    }
}
</code></pre>
<ul>
<li>Servlet3.0 setHttpOnly()  setHttpOnly()</li>
</ul>
<h3 id="使用URL重写"><a href="#使用URL重写" class="headerlink" title="使用URL重写"></a>使用URL重写</h3><ul>
<li>其实就是GET请求参数的应用，当服务器响应浏览器上一次请求时，将某些相关信息以超链接方式响应给浏览器，超链接中包括请求参数。</li>
<li>分页</li>
</ul>
<pre><code class="Java">@WebServlet(&quot;/search&quot;)
public class extends HttpServlet {

...
    String start = request.getParameter(&quot;start&quot;);
    if(start == null) {
        start =&quot;1&quot;;
    }
    int count = Integer.parseInt(start);
    int begin = 10 * count - 9;
    int end = 10 * count;
    out.println(&quot;第 &quot; + begin + &quot; 到 &quot; + end + &quot; 搜索结果&lt;br&gt;&quot;);
    out.println(&quot;&lt;ul&gt;&quot;);
    for(int i = 1; i &lt;= 10 ; i++) {
        out.println(&quot;&lt;li&gt;搜寻结果&quot; + i + &quot;&lt;/li&gt;&quot;);
    }
    out.println(&quot;&lt;/ul&gt;&quot;);
    for(int i = 1; i &lt; 10; i++) {
        if(i == count) {
            out.println(i);
            continue;
        }
        out.println(&quot;&lt;a href=&#39;search?start=&quot; + i + &quot;&#39;&gt;&quot; + i + &quot;&lt;/a&quot;);
    }
...

}
</code></pre>
<h2 id="HttpSession会话管理"><a href="#HttpSession会话管理" class="headerlink" title="HttpSession会话管理"></a>HttpSession会话管理</h2><h3 id="使用HttpSession"><a href="#使用HttpSession" class="headerlink" title="使用HttpSession"></a>使用HttpSession</h3><pre><code class="Java">HttpSession session = request.getSession();

// 另一个版本
// 传入布尔值，默认值时true，表示若尚未存在HttpSession实例时，直接创建一个对象。
// 若传入false，若尚未存在HttpSession实例，则直接返回null

HttpSession setAttribute() getAttribute()
// 这是存放属性对象的第二个地方
// 第三个地方是在ServletContext中
</code></pre>
<ul>
<li>忽略HTTP无状态特性，省略亲手对浏览器发送隐藏域的HTML的操作</li>
<li>如果想在此次会话期间，直接让目前的HttpSession失效，可以执行HttpSession的invalidate()方法(注销机制)，执行invalidate()之后，再次getSession()，取得HttpSession就是另一个新对象了。</li>
<li>HttpSession并非线程安全，所以必须注意属性设定时共享存取的问题。</li>
</ul>
<h3 id="HttpSession会话管理原理"><a href="#HttpSession会话管理原理" class="headerlink" title="HttpSession会话管理原理"></a>HttpSession会话管理原理</h3><ul>
<li>尝试运行 HttpServletRequest 的 getSession 时，Web容器会创建 HttpSession 对象，关键在于每个HttpSession对象都会有个特殊的 ID，Session ID，HttpSession的getId() 来取得Session ID。这个 Session ID 默认会使用Cookie存放在浏览器中。</li>
<li><p>在Tomcat中，Cookie 的名称是 JSESSIONID，数值是getId()所取得的Session ID</p>
</li>
<li><p>由于Web容器本身是执行于JVM中的一个Java程序，getSession()取得HttpSession，是Web容器中的一个Java对象，HttpSession中存放的属性，自然也就存放于服务器端的Web容器之中。</p>
</li>
<li>每一个 HttpSession 各有特殊的 Session ID，当浏览器请求应用程序时，会将 Cookie 中存放的 Session ID 一并发送给应用程序，Web 容器会根据 Session ID来找出对应的HttpSession 对象。</li>
</ul>
<ul>
<li><p>所以使用 HttpSession 来进行会话管理，设定为属性的对象时存储在服务器端，而 Session ID 默认使用 Cookie 存放于浏览器端。Web 容器存储 Session ID 的 Cookie “默认” 为关闭浏览器就实效，所以重新启动浏览器请求应用程序时，通过 getSession() 取得的是新的 HttpSession 对象。</p>
</li>
<li><p>每次请求来到应用程序时，容器会根据发送过来的 Session ID 取得对应的 HttpSession。由于 HttpSession 对象会占用内存空间，所以 HttpSession 的属性中尽量不要存储耗资源的大型对象，必要时将属性移除，或者不需使用 HttpSession 时，执行 invalidate() 让 HttpSession 实效。</p>
</li>
<li><p>默认关闭浏览器会马上失效的是浏览器上的 Cookie，不是 HttpSession。</p>
</li>
<li>要让 HttpSession 立即失效，必须运行 invalidate() 方法，否则，HttpSession 会等到设定的失效期间过后才会被容器销毁回收</li>
</ul>
<pre><code class="Java">// HttpSession的 setMaxInactiveInterval()
// 设定浏览器多久没有请求应用程序的话，浏览器端的HttpSession对象就自动失效，设定的单位是秒。
// web.xml中设定失效时间的单位是分钟。
// 存储Session ID的Cookie默认为关闭浏览器就失效，，而且仅用于存储Session ID
&lt;web-app&gt;
    &lt;session-config&gt;
        &lt;session-timeout&gt;30&lt;/session-timeout&gt;
    &lt;session-config&gt;
&lt;/web-app&gt;
</code></pre>
<ul>
<li>SessionCookieConfig接口，通过实现的对象，可以设定存储Session ID的Cookie相关信息</li>
<li>另一个方法是实现ServletContextListener</li>
</ul>
<h3 id="HttpSession与URL重写"><a href="#HttpSession与URL重写" class="headerlink" title="HttpSession与URL重写"></a>HttpSession与URL重写</h3><ul>
<li><p>用户禁用 Cookie 的情况下，仍打算运用 HttpSession 进行会话管理，可以搭配  重写，向浏览器响应一段超链接，超链接 URL 后附加 Session ID，当用户点击超链接，将 Session ID 以 GET 请求发送给 Web 应用程序</p>
</li>
<li><p>encodeURL() 会自动产生带有 Session ID 的 URL 重写(如果没禁用Cookie，则显示原URL)</p>
</li>
<li><p>使用encodeURL()，在浏览器第一次请求网站时，容器不知道浏览器是否禁用Cookie，容器的做法是Cookie(set-cookie标头)与URL重写的方式。无论浏览器有无禁用Cookie，第一次请求时，都会显示在Session ID的URL</p>
</li>
<li><p>encodeRedirectURL()方法，则可以在要求浏览器重定向时，在URL上显示Session ID</p>
</li>
</ul>
<h2 id="综合练习-1"><a href="#综合练习-1" class="headerlink" title="综合练习"></a>综合练习</h2><h3 id="修改微博应用程序"><a href="#修改微博应用程序" class="headerlink" title="修改微博应用程序"></a>修改微博应用程序</h3><h1 id="Servlet进阶API-过滤器与监听器"><a href="#Servlet进阶API-过滤器与监听器" class="headerlink" title="Servlet进阶API,过滤器与监听器"></a>Servlet进阶API,过滤器与监听器</h1><h2 id="Servlet进阶API"><a href="#Servlet进阶API" class="headerlink" title="Servlet进阶API"></a>Servlet进阶API</h2><h3 id="Servlet-ServletConfig-与-GenericServlet"><a href="#Servlet-ServletConfig-与-GenericServlet" class="headerlink" title="Servlet, ServletConfig 与 GenericServlet"></a>Servlet, ServletConfig 与 GenericServlet</h3><ul>
<li>GenericServlet主要的目的就是将初始Servlet调用 init()方法传入ServletConfig封装起来。</li>
<li>GenericServlet在实现Servlet的 init()方法时，也调用了另一个无参数的init()方法，在编写Servlet时，如果有一些初始时所要运行的动作，可以重新定义这个无参数的init()方法，而不是直接重新定义有ServletConfig参数的init()方法</li>
</ul>
<h3 id="使用ServletConfig"><a href="#使用ServletConfig" class="headerlink" title="使用ServletConfig"></a>使用ServletConfig</h3><pre><code class="Java">@WebServlet(name=&quot;ServletOConfigDemo&quot;, urlPatterns={&quot;/conf&quot;}, 
        initParams={
            @WebInitParam(name = &quot;PARAM1&quot;, value = &quot;VALUE1&quot;),
            @WebInitParam(name = &quot;PARAM2&quot;, value = &quot;VALUE2&quot;)
        }
)
public class ServletConfigDemo extends HttpServlet {
    private String PARAM1;
    private String PARAM2;
    public void init() throws ServletException {
        PARAM1 = getServletConfig().getInitParameter(&quot;PARAM1&quot;);
        PARAM2 = getServletConfig().getInitParameter(&quot;PARAM2&quot;);
    }
    ...
}

OR

&lt;Servlet&gt;
    &lt;Servlet-name&gt;ServletConfigDemo&lt;/Servlet-name&gt;
    &lt;servlet-class&gt;cc.openhome.ServletConfigDemo&lt;/servlet-class&gt;
    &lt;init-param&gt;
        &lt;param-name&gt;PARAM1&lt;/param-name&gt;
        &lt;param-value&gt;VALUE1&lt;/param-value&gt;
    &lt;/init-param&gt;
&lt;/Servlet&gt;
// 若要用web.xml覆盖标注设置，web.xml的&lt;servlet-name&gt;设置必须与@WebServlet的name属性相同

// GenericServlet将ServletConfig封装起来，便于取得设置信息
@WebServlet(name=&quot;ServletOConfigDemo&quot;, urlPatterns={&quot;/conf&quot;}, 
        initParams={
            @WebInitParam(name = &quot;PARAM1&quot;, value = &quot;VALUE1&quot;),
            @WebInitParam(name = &quot;PARAM2&quot;, value = &quot;VALUE2&quot;)
        }
)
public class AddMessage extends HttpServlet {
    private String PARAM1;
    private String PARAM2;
    public void init() throws ServletException {
        PARAM1 = getInitParameter(&quot;PARAM1&quot;);
        PARAM2 = getInitParameter(&quot;PARAM2&quot;);
    }
    ...
}
</code></pre>
<h3 id="ServletContext"><a href="#ServletContext" class="headerlink" title="ServletContext"></a>ServletContext</h3><ul>
<li><p>当整个Web应用程序加载Web容器之后，容器会生成一个ServletContext对象，作为整个应用程序的代表，并设置给ServletConfig，只要通过ServletConfig的 getServletContext()方法就可以取得ServletContext对象。</p>
<ul>
<li><p>getRequestDispatcher()</p>
<blockquote>
<p>取得RequestDispatcher实例<br>  使用时路径的指定必须以“/”作为开头，这个斜杠代表应用程序环境根目录(Context Root)</p>
</blockquote>
</li>
<li><p>getResourcePaths()</p>
<blockquote>
<p>Web应用程序的某个目录中有哪些文件<br>  使用时路径的指定必须以“/”作为开头，表示相对于应用程序环境根目录</p>
</blockquote>
</li>
<li><p>getResourceAsStream()</p>
<blockquote>
<p>读取某个文件的内容</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<h2 id="应用程序事件-监听器"><a href="#应用程序事件-监听器" class="headerlink" title="应用程序事件,监听器"></a>应用程序事件,监听器</h2><h3 id="ServletContext事件，监听器"><a href="#ServletContext事件，监听器" class="headerlink" title="ServletContext事件，监听器"></a>ServletContext事件，监听器</h3><ul>
<li><p>ServletContextListener // 生命周期监听器</p>
<ul>
<li>contextInitialized(ServletContext sce)</li>
<li><p>contextDestroyed(ServletContext sce)</p>
<pre><code class="Java">@WebListener// 没有设置初始参数的属性，需要设置，在Web.xml中设置
public class contextInitialized(ServletContextEvent sce) {
  public void contextInitialized(ServletContextEvent sce) {
      ServletContext context = sce.getServletContext();
      String avatars = context.getInitParameter(&quot;AVATAR&quot;);
      context.setAttribute(&quot;avatars&quot;, avatars);
  }
  public void contextDestroyed(ServletContextEvent sce) {} 
}

ServletContext
  setAttribute()
  getAttribute()
  removeAttribute()
</code></pre>
</li>
</ul>
</li>
<li><p>ServletContextAttributeListener // 监听属性改变的监听器</p>
<ul>
<li>attributeAdded(ServletContextAttributeEvent scab)</li>
<li>attributeRemoved(ServletContextAttributeEvent scab)</li>
<li><p>attributeReplaced(ServletContextAttributeEvent scab)</p>
<pre><code class="Java">&lt;listener&gt;
  &lt;listener-class&gt;cc.openhome.SomeContextAttrListener&lt;/listener-class&gt;
&lt;/listener&gt;
</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="HttpSession事件，监听器"><a href="#HttpSession事件，监听器" class="headerlink" title="HttpSession事件，监听器"></a>HttpSession事件，监听器</h3><ul>
<li><p>HttpSessionListener // 在HttpSession对象创建或结束时</p>
<ul>
<li>sessionCreated(HttpSessionEvent se)</li>
<li>sessionDestroyed(HttpSessionEvent se)</li>
</ul>
</li>
<li><p>HttpSessionAttributeListener // 属性改变监听器</p>
<ul>
<li>attributeAdded(HttpSessionBindingEvent se)</li>
<li>attributeRemoved(HttpSessionBindingEvent se)</li>
<li>attributeReplaced(HttpSessionBindingEvent se)</li>
</ul>
</li>
<li><p>HttpSessionBindingListener // 对象绑定监听器</p>
<ul>
<li>valueBound(HttpSessionBindingEvent event)</li>
<li>valueUnbound(HttpSessionBindEvent event)</li>
</ul>
</li>
<li><p>HttpSessionActivationListener // 对象迁移监听器</p>
<ul>
<li>sessionWillPassivate()</li>
<li>sessionDidActivate()</li>
</ul>
</li>
</ul>
<h3 id="HttpServletRequest事件，监听器"><a href="#HttpServletRequest事件，监听器" class="headerlink" title="HttpServletRequest事件，监听器"></a>HttpServletRequest事件，监听器</h3><ul>
<li><p>ServletRequestListener</p>
<ul>
<li>requestDestroyed(ServletRequestEvent sre)</li>
<li>requestInitialized(ServletRequestEvent sre)</li>
</ul>
</li>
<li><p>ServletRequestAttributeListener</p>
<ul>
<li>attributeAdded(ServletRequestAttributeEvent srae)</li>
<li>attributeRemoved(ServletRequestAttributeEvent srae)</li>
<li><p>attributeReplaced(ServletRequestAttributeEvent srae)</p>
<blockquote>
<p>ServletRequestAttributeEvent<br>  getName()，取得属性设置或移除时指定的名称<br>  getValue()，取得属性设置或移除时的对象</p>
</blockquote>
</li>
</ul>
</li>
<li><p>生命周期监听器与属性改变监听器都必须使用@WebListener或在web.xml中设置，容器才会知道要加载，读取监听器相关设置</p>
</li>
</ul>
<h2 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h2><pre><code>+ 可以真正运行Servlet的service()方法“前”与Servlet的service()方法运行&amp;#39“后”中间进行实现
+ 将服务需求设计为可抽换的元器件
+ 在请求转发时应用过滤器
</code></pre><h3 id="实现与设置过滤器"><a href="#实现与设置过滤器" class="headerlink" title="实现与设置过滤器"></a>实现与设置过滤器</h3><ul>
<li>必须实现Filter接口，并使用@WebFilter标注或在Web.xml中定义过滤器，让容器知道加载哪些过滤器类</li>
<li>Filter<ul>
<li>init(FilterConfig filterConfig) throws ServletException</li>
<li>doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;</li>
<li>destroy();</li>
</ul>
</li>
</ul>
<pre><code class="Java">Filter filter = filterIterator.next();
if(filter != null) {
    filter.doFilter(request, response, this);
}else {
    targetServlet.service(request, response);
}
</code></pre>
<ul>
<li>在陆续调用完Filter实例的doFilter仍至Servlet的service()之后，流程会以堆栈顺序返回，所以在FilterChain的doFilter()运行完毕后，就可以针对service()方法做后续处理</li>
</ul>
<pre><code class="Java">@WebFilter(filterName=&quot;performance&quot;, urlPattern={&quot;/*&quot;})
public class PerformanceFilter implements Filter {
    private FilterConfig config;

    @Override
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long begin = System.currentTimeMillis();
        chain.doFilter(request, response);
        config.getServletContext().log(&quot;Request process in &quot; + (System.currentTimeMillis() - begin) + &quot;milliseconds&quot;);
    }

    @Override
    public void destroy() {}
}
</code></pre>
<ul>
<li>出发过滤器的时机，默认时浏览器直接发出请求。</li>
</ul>
<pre><code class="Java">@WebFilter{
    filterName=&quot;some&quot;,
    urlPatterns={&quot;/some&quot;},
    dispatcherType={
        DispatcherType.FORWARD,
        DispatcherType.INCLUDE,
        DispatcherType.REQUEST,
        DispatcherType.ERROR,
        DispatcherType.ASYNC
    }
}
// 不设置dispatcherTypes,则认为REQUEST
</code></pre>
<ul>
<li>如果同时具备<url-pattern>与<servlet-name>，则先比对<url-pattern>，在比对<servlet-name>。</servlet-name></url-pattern></servlet-name></url-pattern></li>
</ul>
<h3 id="请求封装器"><a href="#请求封装器" class="headerlink" title="请求封装器"></a>请求封装器</h3><ul>
<li><p>实现字符替换过滤器</p>
<blockquote>
<p>HttpServletRequestWrapper实现了HttpServletRequest,继承自ServletRequestWrapper</p>
</blockquote>
<pre><code class="Java">  public class EscapeWrapper extends HttpServletRequestWrapper {
      public EscapeWrapper(HttpServletRequest request) {
          super(request);
      }

      @Override
      public String getParameter(String name) { // 重新定义getParameter方法
          String value = getRequest().getParameter(name);
          return StringEscapeUtils.escapeHtml(value); // Apache Commons Lang／ 进行文字替换
      }
  }
</code></pre>
<pre><code class="Java">  @WebFilter(&quot;/&quot;)
  public class EscapeFilter implements Filter {
      public void init(FilterConfig fConfig) throws ServletException {}
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          HttpServletRequest requestWrapper = new EscapeWrapper((HttpServletRequest) request);
          chain.doFilter(requestWrapper, response);
      }
      public void destroy() {}
  }
</code></pre>
</li>
<li><p>实现编码设置过滤器</p>
<pre><code class="Java">
  public class EncodingWrapper extends HttpServletRequestWrapper {
      private String ENCODING;
      public EncodingWrapper(HttpServletRequest request, String ENCODING) {
          super(request);
          this.ENCODING = ENCODING;
      }

      @Override
      public String getParameter(String name) {
          String value = getRequest().getParameter(name);
          if(value != null) {
              try {
                  byte[] b = value.getBytes(&quot;ISO-8859-1&quot;);
                  value = new String(b, ENCODING);
              }catch(UnsupportedEncodingException e) {
                  throw new RuntimeException(e);
              }
          }
          return value;
      }
  }
</code></pre>
<pre><code class="Java">  @WebFilter(
      urlPattern = { &quot;/*&quot;},
      initParams = {
          @WebInitParam(name = &quot;ENCODING&quot;, value = &quot;UTF-8&quot;)
      }
  )
  public class EncodingFilter implements Filter {
      private String ENCODING;
      public void init(FilterConfig) throws ServletException {
          ENCODING = config.getInitParameter(&quot;ENCODING&quot;);
      }
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          HttpServletRequest req = (HttpServletRequest) request;
          if(&quot;GET&quot;.equals(req.getMethod())) {
              req = new EncodingWrapper(req, ENCODING);
          }else {
              req.setCharacterEncoding(ENCODING);
          }
          chain.doFilter(req, response);
      }
      public void destroy() {}
  }
</code></pre>
<h3 id="响应过滤器"><a href="#响应过滤器" class="headerlink" title="响应过滤器"></a>响应过滤器</h3></li>
<li><p>HttpServletResponseWrapper(父类ServletResponseWrapper)</p>
</li>
<li><p>若要对浏览器进行输出响应，必须通过getWriter()取得PrintWriter，或是通过getOutputStream取得ServletOutputStream</p>
</li>
<li><p>压缩输出</p>
</li>
</ul>
<p>#JSP</p>
<h2 id="从JSP到Servlet"><a href="#从JSP到Servlet" class="headerlink" title="从JSP到Servlet"></a>从JSP到Servlet</h2><h3 id="JSP生命周期"><a href="#JSP生命周期" class="headerlink" title="JSP生命周期"></a>JSP生命周期</h3><pre><code>+ _jspInit(), _jspDestroy(), _jspService(),转移后重新定义。转译后的Servlet是继承自HttpJSPBase类，HttpJspBase类继承自HttpServlet
+ 自定义可以重新定义jspInit(), jspService, jspDestroy
</code></pre><h3 id="Servlet至JSP的简单转换"><a href="#Servlet至JSP的简单转换" class="headerlink" title="Servlet至JSP的简单转换"></a>Servlet至JSP的简单转换</h3><h3 id="指示元素"><a href="#指示元素" class="headerlink" title="指示元素"></a>指示元素</h3><ul>
<li><p>JSP指示(Directive)a元素的主要目的，在于指示容器将JSP转译为Servlet源代码时，一些必须遵守的信息。</p>
<pre><code class="JSP">  &lt;%@ 指示类型 [属性=&quot;值&quot;]* %&gt;
</code></pre>
</li>
<li><p>常用的指示类型：page, include, taglib</p>
<ul>
<li>page指示类型告知容器如何转译目前的JSP网页</li>
<li>include指示类型告知容器将别的JSP页面包括进来进行转译</li>
<li>taglib指示类型告知容器如何转译这个页面的标签库(Tag Library)</li>
</ul>
</li>
<li><p>指示元素中可以有多对的属性/值，必要时，同一个指示类型可以数个指示元素来设置。</p>
<ul>
<li><p>page</p>
<pre><code class="JSP">  &lt;%@page import=&quot;java.util.Date&quot; %&gt;
  &lt;%@page contentType=&quot;text/html&quot; pageEncoding=&quot;UTF-8&quot;%&gt;

  //import属性转译后在Servlet源代码中
  &lt;%@page import=&quot;java.util.Date,cc.openhome.*&quot; %&gt;

  //response.setContentType(&quot;text/html;charset=UTF8&quot;);
  //可以编写在同一个元素中
</code></pre>
<ul>
<li>page指示类型其他属性<ul>
<li>info</li>
<li>autoFlush</li>
<li>buffer</li>
<li>errorPage</li>
<li>extends</li>
<li>isErrorPage</li>
<li>language</li>
<li>session</li>
</ul>
</li>
</ul>
</li>
<li><p>include</p>
<pre><code class="JSP">  &lt;%@page contentType=&quot;text/html&quot; pageEncoding=&quot;UTF-8&quot; %&gt;
  &lt;%@include file=&quot;/WEB-INF/jspf/header.jspf&quot; %&gt;
  //在转译期间就决定转译后的Servlet内容，静态包括方式
  //&lt;jsp:include&gt;标签的使用，则是运行时动态包括别的网页执行流程进行相应的方式，使用&lt;jsp:include&gt;的网页与被&lt;jsp:include&gt;包括的网页，各自都生成一个独立的Servlet
</code></pre>
</li>
</ul>
</li>
<li><p>可以在web.xml中统一默认的网页编写，内容类型，缓冲区大小等。</p>
<pre><code class="XML">  &lt;web-app&gt;
      &lt;jsp-config&gt;
          &lt;jsp-property-group&gt;
              &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
              &lt;page-encoding&gt;UTF-8&lt;/page-encoding&gt;
              &lt;default-content-type&gt;text/html&lt;/default-content-type&gt;
              &lt;buffer&gt;16kb&lt;/buffer&gt;
          &lt;/jsp-property-group&gt;
      &lt;/jsp-config&gt;
  &lt;/web-app&gt;

  //声明JSP的开头与结尾
  &lt;web-app&gt;
      &lt;jsp-config&gt;
          &lt;jsp-property-group&gt;
              &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
              &lt;include-prelude&gt;/WEB-INF/jspf/pre.jspf&lt;/include-prelude&gt;
              &lt;include-coda&gt;/WEB-INF/jspf/coda.jspf&lt;include-coda&gt;
          &lt;/jsp-property-group&gt;
      &lt;/jsp-config&gt;
  &lt;/web-app&gt;
</code></pre>
</li>
</ul>
<h3 id="声明，Scriptlet与表达式元素"><a href="#声明，Scriptlet与表达式元素" class="headerlink" title="声明，Scriptlet与表达式元素"></a>声明，Scriptlet与表达式元素</h3><pre><code>```JSP
//声明(Declaration)元素
&lt;%! 类成员声明或方法声明 %&gt;
// 转译成Servlet中的类成员或方法
//小心数据共享与线程安全的问题
//jspInit()方法，jspDestroy()的方法通过这个进行初始化操作

//Scriptlet元素
&lt;% Java语句 %&gt;
//转译后为Servlet源代码_jspService()方法中的内容

// 表达式元素
&lt;%= Java表达式 %&gt;
&lt;%= new Date() %&gt;
// 表达式元素中不要加上分号(;)
//转译Servlet
out.print(new Date());//表达式中的内容会直接转译为out对象输出时的指定内容
```
</code></pre><ul>
<li>输出&lt;%或%&gt;符号，要将角括号置换为其他字符。&lt;%-&lt;%  %&gt;-%&gt;或%></li>
<li>禁用JSP的Scriptlet，在web.xml中设置<scripting-invalid>true&lt;/..</scripting-invalid></li>
</ul>
<h3 id="注释元素"><a href="#注释元素" class="headerlink" title="注释元素"></a>注释元素</h3><ul>
<li>使用Java注释的方式(// /<em>…</em>/)，在Servlet源代码中也有对应的注释文字</li>
<li>使用HTML网页使用的注释方式<!-- -->，HTML源代码中有</li>
<li>使用JSP专门的注释&lt;%– JSP注释 –%&gt;，转译到Servlet中不会包括注释文字，也不会输出到浏览器</li>
</ul>
<h3 id="隐式对象-隐式变量"><a href="#隐式对象-隐式变量" class="headerlink" title="隐式对象/隐式变量"></a>隐式对象/隐式变量</h3><ul>
<li><p>out: 转译后对应JspWriter对象，其内部关联一个PrintWriter对象</p>
<ul>
<li>JspWriter直接继承于java.io.Writer类。JspWriter主要模拟了BufferedWriter与PrintWriter的功能</li>
</ul>
</li>
<li><p>request: 转译后对应HttpServletRequest对象</p>
</li>
<li>response: 转译后对应HttpServletResponse对象</li>
<li>config: 对应ServletConfig对象</li>
<li>application: 对应ServeltContext对象</li>
<li>session: 对应HttpSession对象</li>
<li><p>pageContext: 对应PageContext对象，它提供了JSP页面资源的封装，并可设置页面范围属性</p>
<ul>
<li>javax.servlet.jsp.PageContext</li>
<li>所有隐式参数都可以通过pageContext来取得。</li>
<li>设置页面范围属性(setAttribute(),getAttribute(),removeAttribute())</li>
<li><p>页面范围属性表示作用范围仅限同一个页面中</p>
<pre><code class="java">&lt;%
  Some some = pageContext.getAttribute(&quot;some&quot;);
  if(some == null) {
      some = new Some();
      pageContext.setAttribute(&quot;some&quot;, some);
  }
%&gt;

// 通过pageContext设置四种范围属性
getAttribute(String name, int scope)
setAttribute(String name, Object value, int scope)
removeAttribute(String name, int scope)

//scope常量：pageContext.PAGE_SCOPE, pageContext.REQUEST_SCOPE, pageContext.SESSION_SCOPE, pageContext.APPLICATION_SCOPE

//不知道属性范围
pageContext.findAttribute();//依次从页面，请求，会话，应用程序范围寻找有无对应的属性，先找到就先返回

Object attr = pageContext.findAttribute(&quot;attr&quot;);
</code></pre>
</li>
</ul>
</li>
<li><p>exception: 对应Throwable对象，代表由其他JSP页面抛出的异常对象，只会出现与JSP错误页面(isErrorPage设置为true的JSP页面)</p>
</li>
<li><p>page: 对应this</p>
</li>
<li><p>隐式对象时只能在&lt;%与%&gt;之间，或&lt;%=与%&gt;之间使用，转译后Servlet后，是_jspService()中的局部变量。</p>
</li>
</ul>
<h3 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h3><ul>
<li><p>错误发生在三个时候</p>
<ul>
<li>JSP转换为Servlet源代码时。</li>
<li>Servlet源代码进行编译时。</li>
<li>Servlet载入容器进行服务但发生运行错误时。</li>
</ul>
</li>
<li><p>只有在isErrorPage设置为true的页面才可以使用exception隐式对象。</p>
<ul>
<li>&lt;%=exception %&gt;</li>
<li><p>打印异常堆栈跟踪，printStackTrace()接受一个PrintWriter对象作为参数，out构造PrintWriter</p>
<pre><code class="xml">// 异常对象
&lt;web-app&gt;
  &lt;error-page&gt;
      &lt;exception-type&gt;java.lang.NullPointerException&lt;/exception-type&gt;
      &lt;location&gt;/report.view&lt;/location&gt;
  &lt;/error-page&gt;
&lt;/web-app&gt;

// HTTP错误状态码
&lt;web-app&gt;
  &lt;error-page&gt;
      &lt;error-code&gt;404&lt;/error-code&gt;
      &lt;location&gt;/404.jsp&lt;/location&gt;
  &lt;/error-page&gt;
&lt;/web-app&gt;
</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="标准标签-Standard-Tag"><a href="#标准标签-Standard-Tag" class="headerlink" title="标准标签(Standard Tag)"></a>标准标签(Standard Tag)</h2><ul>
<li>所有容器都支持这些标签，可协助编写JSP时减少Scriptlet的使用。所有标准标签都使用jsp:作为前置</li>
</ul>
<h3 id="lt-jsp-include-gt-lt-jsp-forward-gt-标签"><a href="#lt-jsp-include-gt-lt-jsp-forward-gt-标签" class="headerlink" title="&lt;jsp:include&gt;, &lt;jsp:forward&gt;标签"></a><code>&lt;jsp:include&gt;</code>, <code>&lt;jsp:forward&gt;</code>标签</h3><h3 id="lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt-简介"><a href="#lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt-简介" class="headerlink" title="&lt;jsp:useBean&gt;, &lt;jsp:setProperty&gt;与&lt;jsp:getProperty&gt;简介"></a><code>&lt;jsp:useBean&gt;</code>, <code>&lt;jsp:setProperty&gt;</code>与<code>&lt;jsp:getProperty&gt;</code>简介</h3><ul>
<li><p><jsp:usebean>标签是用来搭配JavaBean元件的标准标签。</jsp:usebean></p>
</li>
<li><p>JavaBean</p>
<ul>
<li>必须实现java.io.Serializable接口</li>
<li>没有公共(public)的类变量</li>
<li>具有无参数的构造器</li>
<li>具有公开的设值方法(Setter)与取值方法(Getter)</li>
</ul>
</li>
</ul>
<h3 id="深入-lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt"><a href="#深入-lt-jsp-useBean-gt-lt-jsp-setProperty-gt-与-lt-jsp-getProperty-gt" class="headerlink" title="深入&lt;jsp:useBean&gt;, &lt;jsp:setProperty&gt;与&lt;jsp:getProperty&gt;"></a>深入<code>&lt;jsp:useBean&gt;</code>, <code>&lt;jsp:setProperty&gt;</code>与<code>&lt;jsp:getProperty&gt;</code></h3><ul>
<li>在使用<code>&lt;    jsp:useBean&gt;</code>标签取得或创建JavaBean实例之后，由于<code>&lt;jsp:setProperty&gt;</code>与<code>&lt;jsp:getProperty&gt;</code>转译后的，都是使用PageContext的findAttribute()来寻找属性，因此寻找的顺序是页面，请求，会话，应用程序范围。</li>
</ul>
<h3 id="谈谈Model1"><a href="#谈谈Model1" class="headerlink" title="谈谈Model1"></a>谈谈Model1</h3><h3 id="XML格式标签"><a href="#XML格式标签" class="headerlink" title="XML格式标签"></a>XML格式标签</h3><h2 id="表达式语言-EL"><a href="#表达式语言-EL" class="headerlink" title="表达式语言(EL)"></a>表达式语言(EL)</h2><h3 id="EL简介"><a href="#EL简介" class="headerlink" title="EL简介"></a>EL简介</h3><ul>
<li>以进行属性，请求参数，标头与Cookie等信息的取得，或一些简单的运算或判断，试着用EL来代替</li>
</ul>
<pre><code class="JSP">&lt;%
    String a = request.getParameter(&quot;a&quot;);
    String b = request.getParameter(&quot;b&quot;);
    out.println(&quot;a + b = &quot; + (Integer.parseInt(a) + Integer.parseInt(b)));
%&gt;

${param.a} + ${param.b} = ${param.a + param.b}
// param是EL隐式对象之一，表示用户的请求参数
//对于null值直接以空字符串加以显示

&lt;%= ((HttpServletRequest) pageContext.getRequest()).getMethod() %&gt;
${pageContext.request.method}
</code></pre>
<ul>
<li><p>page指示元素的isELIgnored属性(默认是false)，来设置是否使用EL。原因在于，网页中已含有与EL类似的${}语法功能存在</p>
</li>
<li><p>web.xml是2.3或以下版本不支持EL</p>
</li>
</ul>
<h3 id="使用EL取得属性"><a href="#使用EL取得属性" class="headerlink" title="使用EL取得属性"></a>使用EL取得属性</h3><pre><code>```JSP
&lt;h1&gt;&lt;jsp:property name=&quot;user&quot; property=&quot;name&quot;&gt;登录成功&lt;h1&gt;

&lt;h1&gt;${user.name&gt;登录成功&lt;/h1&gt;

// EL隐式对象指定范围来存取属性。
```
</code></pre><ul>
<li>运算符说明<ul>
<li>如果使用点(.)运算符，则左边可以是JavaBean或Map对象</li>
<li>如果使用[]运算符，则左边可以是JavaBean，Map，数组或List对象</li>
</ul>
</li>
</ul>
<h3 id="缺页"><a href="#缺页" class="headerlink" title="缺页"></a>缺页</h3><h3 id="EL运算符"><a href="#EL运算符" class="headerlink" title="EL运算符"></a>EL运算符</h3><ul>
<li><p>EL可以直接进行一些算术运算，逻辑运算与关系运算。</p>
</li>
<li><p>除法(/或div)，求模(%或mod)，小于(&lt;或lt(Less-than))，大于(&gt;或gt(Greater-than))，小于或等于(&lt;=或le)，大于或等于(&gt;=或ge(Greater-than-or-equal))，等于(==或eq(Equal))，不等于(!=或ne(Not-equal))</p>
</li>
<li><p>自定义EL函数</p>
</li>
</ul>
<pre><code class="JSP">&lt;%= Util.length(request.getAttribute(&quot;someList))%&gt;
${ util: length(requestScope.someList) }
</code></pre>
<ul>
<li>自定义EL函数的第一步是编写类，它必须是个公共类，而想要调用的方法必须是公开且为静态方法。</li>
<li><p>必须编写一个标签程序库描述(TLD)文件，这个文件是个XML文件，后缀为*.tld，让Web容器知道怎么将这个类中的length()方法当做EL函数来使用。</p>
<pre><code class="XML">  //openhome.tld
  &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
  &lt;taglib version=&quot;2.1&quot; xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
      xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
      xsi:schemaLication=&quot;http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd&quot;&gt;
      &lt;tlib-verion&gt;1.0&lt;/tlib-version&gt;
      &lt;short-name&gt;openhome&lt;/short-name&gt;
      &lt;uri&gt;http://openhome.cc/util&lt;/url&gt;//JSP网页中会用到
      &lt;function&gt;
          &lt;description&gt;Collection Length&lt;/description&gt;
          &lt;name&gt;length&lt;/name&gt;
          &lt;function-class&gt;
              cc.openhome.Util
          &lt;/function-class&gt;
          &lt;function-signature&gt;
              int length(java.util.Collection)
          &lt;/function-signature&gt;
      &lt;/function&gt;
  &lt;/taglib&gt;

  //elfunction.jsp
  &lt;%@page contentType=&quot;text/html&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
  &lt;%@taglib prefix=&quot;util&quot; uri=&quot;http://openhome.cc/util&quot;%&gt;
</code></pre>
</li>
</ul>
<h3 id="复习-1"><a href="#复习-1" class="headerlink" title="复习"></a>复习</h3><ul>
<li><code>&lt;jsp:include&gt;</code>或<code>&lt;jsp:forward&gt;</code>标签，在转译为Servlet源代码之后，底层也是取得RequestDispatcher对象，并执行对应的forward()或include()方法</li>
</ul>
<h1 id="使用JSTL"><a href="#使用JSTL" class="headerlink" title="使用JSTL"></a>使用JSTL</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><ul>
<li><p>JSTL标签库</p>
<ul>
<li>核心标签库：提供条件判断，属性访问，URL处理及错误处理等标签。</li>
<li>I18N兼容格式标签库：提供数字，日期等的格式化功能，以及区域(Locale)，信息，编码处理等国际化功能的标签。</li>
<li>SQL标签库：提供基本的数据库查询，更新，设置数据源(DataSource)等功能的标签.</li>
<li>XML标签库：提供XML解析，流程控制，转换等功能的标签。</li>
<li><p>函数标签库：提供常用字符串处理的自定义EL函数标签库。</p>
<pre><code class="XML">&lt;taglib&gt;
  &lt;taglib-uri&gt;http://java.sun.com/jstl/core/&lt;/taglib-uri&gt;
  &lt;taglib-location&gt;/WEB-INF/tlds/c.tld&lt;/taglib-location&gt;
&lt;/taglib&gt;

//JSP
&lt;%@taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jstl/core&quot;%&gt;
</code></pre>
</li>
</ul>
</li>
</ul>
<h2 id="核心标签库"><a href="#核心标签库" class="headerlink" title="核心标签库"></a>核心标签库</h2><h3 id="流程处理标签"><a href="#流程处理标签" class="headerlink" title="流程处理标签"></a>流程处理标签</h3><pre><code class="JSP">&lt;c:if test=&quot;${param.name == &#39;momor&#39; &amp;&amp; param.password == &#39;1234&#39;}&quot;&gt;
    &lt;h1&gt;${param.name} 登录成功&lt;/h1&gt;
&lt;/c:if&gt;

&lt;c:choose&gt;
    &lt;c:when test=&quot;${user.valid}&quot;&gt;
        &lt;h1&gt;
            &lt;jsp:getProperty name=&quot;user&quot; property=&quot;name&quot;/&gt;登录成功
        &lt;/h1&gt;
    &lt;/c:when&gt;
    &lt;c:otherwise&gt;
        &lt;h1&gt;登录失败&lt;/h1&gt;
    &lt;/c:otherwise&gt;
&lt;/c:choose&gt;
// &lt;c:when&gt;,&lt;c:otherwise&gt;必须放在&lt;c:choose&gt;中
// &lt;c:choose&gt;中可以有多个&lt;c:when&gt;,遇到true就输出

&lt;c:forEach var=&quot;message&quot; items=&quot;${messageService.messages}&quot;&gt;
    &lt;tr&gt;
        &lt;td&gt;${message.name}&lt;/td&gt;
        &lt;td&gt;${message.text}&lt;/td&gt;
    &lt;/tr&gt;
&lt;/c:forEach&gt;
// items属性可以是数组，Collection，Iterator，Enumeration，Map与字符串

//items指定的是Map
//则设置给var的对象会是Map.Entry，这个对象有getKey()与getValue()方法，可以让你取得键与值
&lt;c:forEach var=&quot;item&quot; items=&quot;${someMap}&quot;&gt;
    Key: ${item.key}&lt;br&gt;
    Value: ${item.value}&lt;br&gt;
&lt;/c:forEach&gt;

//items指定字符串，则必须是个以逗号区隔的值，&lt;c:forEach&gt;会自动以逗号来切割字符串
&lt;c:forEach var=&quot;token&quot; items=&quot;Java,C++,C,JavaScript&quot;&gt;
    ${token} &lt;br&gt;
&lt;/c:forEach&gt;

//自行指定切割依据，使用&lt;c:forTokens&gt;
&lt;c:forTokens var=&quot;tolen&quot; delims=&quot;:&quot; items=&quot;Java:C++:C:JavaScript&quot;&gt;
    ${token} &lt;br&gt;
&lt;/c:forTokens&gt;
</code></pre>
<h3 id="错误处理标签"><a href="#错误处理标签" class="headerlink" title="错误处理标签"></a>错误处理标签</h3><ul>
<li>在目前网页捕捉异常，并显示相关信息。Java的try-catch / <code>&lt;c:catch&gt;</code></li>
</ul>
<pre><code class="JSP">&lt;c:catch var=&quot;error&quot;&gt;
    ${param.a} + ${param.b} = ${param.a + param.b}
&lt;/c:catch&gt;
&lt;c:if test=&quot;${error != null}&quot;&gt;
    &lt;br&gt;&lt;span style=&quot;color: red;&quot;&gt;${error.message}&lt;/span&gt;
    &lt;br&gt;${error}
&lt;/c:if&gt;
</code></pre>
<h3 id="网页导入，重定向，URL处理标签"><a href="#网页导入，重定向，URL处理标签" class="headerlink" title="网页导入，重定向，URL处理标签"></a>网页导入，重定向，URL处理标签</h3><ul>
<li><p><code>&lt;c:import&gt;</code>标签，可以视作是<code>&lt;jsp:include&gt;</code>的加强版，可以在运行时动态导入另一个网页，也可以搭配<code>&lt;c:param&gt;</code>在导入另一网页时带有参数。</p>
<pre><code class="JSP">  &lt;c:import url=&quot;add.jsp&quot;&gt;
      &lt;c:param name=&quot;a&quot; value=&quot;1&quot; /&gt;
      &lt;c:param name=&quot;b&quot; value=&quot;2&quot; /&gt;
  &lt;/c:import&gt;

  // &lt;c:import&gt;标签还好可以导入非目前Web应用程序中的网页
  &lt;c:import url=&quot;http://openhome.cc&quot; charEncoding=&quot;BIG5&quot; /&gt;
</code></pre>
</li>
<li><p><code>&lt;c:redirect&gt;</code>标签，重定向需要参数，也可以通过<c:param>来设置</c:param></p>
</li>
<li><p>url重写<code>&lt;c:url&gt;</code></p>
<pre><code class="JSP">  &lt;c:url value=&#39;count.jsp&#39; /&gt;
</code></pre>
</li>
</ul>
<h3 id="属性处理与输出标签"><a href="#属性处理与输出标签" class="headerlink" title="属性处理与输出标签"></a>属性处理与输出标签</h3><ul>
<li><p><code>&lt;c:set&gt;</code></p>
</li>
<li><p><code>&lt;c:remove&gt;</code></p>
</li>
<li><p><code>&lt;c:out&gt;</code>，属性escapeXml(自动将角括号，单引号，双引号等字符用替代字符取代。)默认为true。</p>
</li>
<li><p>EL运算结果为null时，并不会显示任何值。希望在EL为null时，显示一个默认值。</p>
<pre><code class="JSP">  &lt;c:choose&gt;
      &lt;c:when test=&quot;${param.a != null}&quot;&gt;
          ${param.a}
      &lt;/c:when&gt;
      &lt;c:otherwise&gt;
          0
      &lt;/c:otherwise&gt;
  &lt;/c:choose&gt;

  //或:
  &lt;c:out value=&quot;${param.a}&quot; default=&quot;0&quot; /&gt;
</code></pre>
</li>
</ul>
<h2 id="I18N兼容格式标签库"><a href="#I18N兼容格式标签库" class="headerlink" title="I18N兼容格式标签库"></a>I18N兼容格式标签库</h2><ul>
<li>如果一个应用程序在设计时，可以在不修改应用程序的情况下，根据不同的用户直接采用不同的语言，数字格式，日期格式等，这样的设计考量称为国际化。(I18N，因为internationalization有18个字母)</li>
</ul>
<h3 id="I18N基础"><a href="#I18N基础" class="headerlink" title="I18N基础"></a>I18N基础</h3><ul>
<li><p>Java的字符串是Unicode，<code>java -encoding UTF-8 Main.java</code></p>
</li>
<li><p>java.util.ResourceBundle消息绑定</p>
<pre><code class="Java">  // messages.properties// 放在Classpath的路径设置下
  cc.openhome.welcome=Hello
  cc.openhome.name=World

  // Hello.java
  public class Hello {
      public static void main(String[] args) {
          ResourceBundle res = ResourceBundle.getBundle(&quot;messagea&quot;);// 自动找对应的.properties
          System.out.print(res.getString(&quot;cc.openhome.welcome&quot;) + &quot;!&quot;);
          System.out.println(res.getString(&quot;cc.openhome.name&quot;) + &quot;!&quot;);
      }
  }
</code></pre>
</li>
<li><p>关于国际化</p>
</li>
<li><p>地区(Locale)信息，资源包(Resource bundle)与基础名称(Base name)</p>
</li>
<li><p>语言编码由两个小写字母代表，地区编码由两个大写字母代表</p>
<pre><code class="Java">  Locale locale = new Locale(&quot;zh&quot;, &quot;CN&quot;);
  ResourceBundle res = ResourceBundle.getBundle(&quot;messages&quot;, locale);
</code></pre>
</li>
</ul>
<h3 id="信息标签"><a href="#信息标签" class="headerlink" title="信息标签"></a>信息标签</h3><pre><code class="JSP">&lt;%@taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot;%&gt;
&lt;fmt:bundle basename=&quot;messages1&quot;&gt;
&lt;fmt:message key=&quot;cc.openhome.title&quot; /&gt;

//&lt;fmt:setBundle&gt;标签设置basename属性，设置的作用域是整个页面都有作用。如果额外有&lt;fmt:bundle&gt;设置，则会以&lt;fmt:bundle&gt;的设置为主。
</code></pre>
<ul>
<li><p>占位符</p>
<pre><code class="JSP">  // messages3.properties
  cc.openhome.forUser=Hi! {0}! It is {1, date, long} and {2, time, full}

  fmt3.jsp
  &lt;jsp:useBean id=&quot;now&quot; class=&quot;java.util.Date&quot;/&gt;
  &lt;fmt:setBundle basename=&quot;messages3&quot;/&gt;

      &lt;fmt:message key=&quot;cc.openhome.forUser&quot;&gt;
          &lt;fmt:param value=&quot;${param.username}&quot;/&gt;
          &lt;fmt:param value=&quot;${now}&quot;/&gt;
          &lt;fmt:param value=&quot;${now}&quot;/&gt;
      &lt;/fmt:message&gt;
</code></pre>
</li>
</ul>
<h3 id="地区标签"><a href="#地区标签" class="headerlink" title="地区标签"></a>地区标签</h3><pre><code>```JSP
&lt;fmt:setLocale value=&quot;zh-TW&quot;/&gt; // 调用HttpServletResponse的setLocale()
&lt;fmt:setBundle basename=&quot;hello&quot;/&gt;

&lt;fmt:message key=&quot;cc.openhome.hello&quot;/&gt;

// 如果使用了setCharacterEncoding()或setContentType()时指定了charset,则setLocale()就会被忽略。
// &lt;fmt:requestEncoding&gt;设置编码，就会调用HttpServletRequest的setCharacterEncoding()，必须在取得任何请求参数之前使用

&lt;%
    ResourceBundle zh_TW = ResourceBundle.getBundle(&quot;hello&quot;, new Locale(&quot;zh&quot;, &quot;TW&quot;));
    pageConetext.setAttribute(&quot;zh_TW&quot;, new LocalozationContext(zh_TW));
%&gt;
&lt;fmt:message bundle=&quot;${zh_TW}&quot; key=&quot;cc.openhome.hello&quot;/&gt;

// &lt;fmt:message&gt;标签有个bundle属性，可用以指定LocalizationContext对象，可以在创建LocalizationContext对象时指定ResourceBundle与Locale对象。
//共享Locale信息，可以使用&lt;fmt:setLocale&gt;标签，value属性上指定地区信息。
```
</code></pre><ul>
<li>JSTL规格书</li>
</ul>
<h3 id="格式标签"><a href="#格式标签" class="headerlink" title="格式标签"></a>格式标签</h3><ul>
<li><p>针对数字，日期与时间，搭配地区设置或指定的格式来进行格式化，也可以进行数组，日期与时间的解析。</p>
<pre><code class="JSP">  &lt;jsp:useBean id=&quot;now&quot; class=&quot;java.util.Date&quot;/&gt;

  &lt;fmt:formatDate value=&quot;${now}&quot; dateStyle=&quot;full&quot; type=&quot;time&quot; timeStyle=&quot;full&quot; pattern=&quot;dd.MM.yy&quot;/&gt;
  // dateStyle，日期的详细程度，可设置的值有&quot;default&quot;,&quot;short&quot;,&quot;medium&quot;,&quot;long&quot;,&quot;full&quot;
  // type，如果显示时间，则要在type属性指定&quot;time&quot;或&quot;both&quot;,默认是&quot;date&quot;
  // timeStyle，时间的详细程度，可设置的值有&quot;default&quot;,&quot;short&quot;,&quot;medium&quot;,&quot;long&quot;,&quot;full&quot;
  // pattern，自定义格式

  &lt;fmt:timeZone value=&quot;GMT+1:00&quot;&gt;
      &lt;fmt:formatDate value=&quot;${now}&quot; type=&quot;both&quot; dateStyle=&quot;full&quot; timeStyle=&quot;full&quot;/&gt;
  &lt;/fmt:timeZone&gt;
  // &lt;fmt:timeZone&gt;可指定时区
  //需要全局指定时区，&lt;fmt:setTImeZone&gt;
  // 没有指定地区会根据浏览器的Accept-Language标头来决定地区

  &lt;fmt:formatNumber value=&quot;12345.678&quot; type=&quot;currency&quot; currencySymbol=&quot;新台币&quot; pattern=&quot;#,#00.0#&quot;&gt;
  //type，可设置值&quot;number&quot;(默认), &quot;currency&quot;(数字按照货币格式进行格式化), &quot;percent&quot;(百分比)
  //currencySymbol，可指定货币符号
</code></pre>
</li>
<li><p>解析<code>&lt;fmt:parseDate&gt;</code>,<code>&lt;fmt:parseNumber&gt;</code></p>
</li>
<li><p>格式化标签寻找地区信息的顺序是：</p>
<ol>
<li>使用<fmt:bundle>指定的地区信息</fmt:bundle></li>
<li>寻找LocalizationContext中的地区信息，也就是属性范围中有无javax.servlet.jsp.jstl.fmt.localizationContext属性</li>
<li>使用浏览器Accept-Language标头指定的偏好地区</li>
<li>使用后备地区信息</li>
</ol>
</li>
</ul>
<h2 id="XML标签库"><a href="#XML标签库" class="headerlink" title="XML标签库"></a>XML标签库</h2><h2 id="函数标签库"><a href="#函数标签库" class="headerlink" title="函数标签库"></a>函数标签库</h2><pre><code class="JSP">&lt;%@taglib prefix=&quot;fn&quot; uri=&quot;http://java.sun.com/jsp/jstl/function&quot;%&gt;
${fn:length(param.text)}
</code></pre>
<ul>
<li>字符串处理相关好函数<ul>
<li>改变字符串大小写：toLowerCase, toUpperCase</li>
<li>取得子字符串：substring, substringAfter, substringBefore</li>
<li>裁剪字符串前后空白：trim</li>
<li>字符串取代： replace</li>
<li>检查是否包括子字符串：startsWith, endsWith, contains, containsIgnoreCase</li>
<li>检查子字符串位置：indexOf</li>
<li>切割字符串为字符串数组：split</li>
<li>连接字符串数组为字符串：join</li>
<li>替换XML字符：escapeXML</li>
</ul>
</li>
</ul>
<h1 id="自定义标签"><a href="#自定义标签" class="headerlink" title="自定义标签"></a>自定义标签</h1><h1 id="整合数据库"><a href="#整合数据库" class="headerlink" title="整合数据库"></a>整合数据库</h1><h2 id="JDBC入门"><a href="#JDBC入门" class="headerlink" title="JDBC入门"></a>JDBC入门</h2><h3 id="JDBC简介"><a href="#JDBC简介" class="headerlink" title="JDBC简介"></a>JDBC简介</h3><ul>
<li><p>编写的应用程序是利用网络通信协议与数据库进行命令交换，以进行数据的增删改查。通常应用程序会利用一组专门与数据库进行通行协议的程序库，以简化与数据库沟通是的程序编写。</p>
</li>
<li><p>JDBC(Java DataBase Connectivity)Java数据库连接的标准规范。定义了一组标准类与接口，应用程序需要连接数据库时就调用这组标准API，而标准API中的接口会由数据库厂商实现，通常称为JDBC驱动程序(Driver)。</p>
</li>
<li><p>JDBC标准主要分为两个部分：JDBC应用程序开发者接口以及驱动程序开发者接口。</p>
<ul>
<li>应用程序开发者接口相关API主要在java.sql与javax.sql两个包中</li>
<li>JDBC驱动程序开发者接口则是数据库厂商要实现驱动程序时的规范。</li>
</ul>
</li>
<li><p>要取得数据库连接，几个操作</p>
<ol>
<li><p>注册Driver实现对象</p>
<ul>
<li><p>使用JDBC时，要求加载.class文件的方式有四种：</p>
<ul>
<li><p>使用Class.forName()</p>
<pre><code class="Java">  //动态加载驱动程序类
  try {
      Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
  }catch(ClassNotFoundException e) {
      throw new RuntimeException(&quot;找不到指定的类&quot;)；
  }

  // MySQL的Driver类实现源代码
  public class Driver extends NonRegisteringDriver implements java.sql.Driver {
      static {
          try {
              // 注册Driver实例的操作
              java.sql.DriverManager.registerDriver(new Driver());
          }catch(SQLException E) {
              throw new RuntimeException(&quot;Can&#39;t register driver!&quot;);
          }
      }
  }
  public Driver() throws SQLException {}
</code></pre>
</li>
<li><p>自行创建Driver接口实现类的实例</p>
<pre><code class="Java">  java.sql.Driver driver = new com.mysql.jdbc.Driver();
</code></pre>
</li>
<li><p>启动JVM时指定jdbc.drivers属性</p>
<pre><code class="shell">  java -Djdbc.drivers=com.mysql.jdbc.Driver;ooo.XXXDriver YourProgram
</code></pre>
</li>
<li><p>设置JAR中 /services/java.sql.Driver文件</p>
<ul>
<li>Java SE 6之后JDBC 4.0的新特性，只要在驱动程序实现的JAR文件/services文件夹中，放置一个java.sqlDriver文件，当中编写Driver接口的实现类名称全名，DriverManager会自动读取这个文件并找到指定类进行注册。</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>取得Connection实现对象</p>
<pre><code class="Java"> // Connection接口的实现对象，是数据库连接代表对象。
 Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
 // JDBC URL 定义了连接数据库时的协议，子协议，数据源标识：
 // 协议：子协议：数据源标识
 // 数据源标识标出数据库的地址，端口，名称，用户，密码等信息
 // mysql
 jdbc:mysql://主机名称:连接端口/数据库名称？参数=值&amp;参数=值

 // 主机名称可以是本机(localhost)或其他连接主机名称，地址。Mysql连接端口默认为3306。
 jdbc:mysql://localhost:3306/demo?usere=root&amp;password=123456

 // 如果要使用中文访问(假设数据库表格编码使用UTF-8)
 jdbc:mysql://localhost:3306/demo?user=root&amp;password=123&amp;useUnicode=true&amp;characterEncoding=UTF8

 // 有时会将JDBC URL编写在XML配置文档中，此时不能直接在XML中写&amp;符号，而必须改写为&amp;amp;代替字符
 jdbc:mysql://localhost:3306/demo?user=root&amp;password=123&amp;userUnicode=true&amp;amp;
 characterEncoding=UTF8
</code></pre>
<ul>
<li><p>直接通过DriverManager的getConnection()连接数据库</p>
<pre><code class="Java">  Connection conn = null;
  SQLException ex = null;
  try {
      String url = &quot;jdbc:mysql://localhost:3306/demo&quot;;
      String user = &quot;root&quot;;
      String password = &quot;123456&quot;;
      conn = DriverManager.getConnection(url, user, password);
      ...
  }
  catch(SQLException e) {
      ex = e
  }
  finally {
      if(conn != null) {
          try {
              conn.close();
          }
          catch(SQLException e) {
              if(ex == null) {
                  ex = e;
              }
          }
      }
      if(ex != null) {
          throw new RuntimeException(ex);
      }
  }

  // SQLException 是受检异常(Checked Exception)必须使用try... catch明确处理，在异常发生时尝试关闭相关资源

  // SQLException有个子类SQLWarning(警示信息)
</code></pre>
</li>
</ul>
</li>
<li><p>关闭Connection实现对象</p>
<ul>
<li>取得Connection对象之后，可以使用isClosed()方法测试与数据库的连接是否关闭。</li>
<li><p>在操作完数据库之后，若确定不再需要连接，则必须使用close()关闭与数据库的连接，以释放连接时相关的必要资源，如：连接相关对象，授权资源。</p>
</li>
<li><p>DriverManager中相关源码</p>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="使用Statement，ResultSet"><a href="#使用Statement，ResultSet" class="headerlink" title="使用Statement，ResultSet"></a>使用Statement，ResultSet</h3><ul>
<li><p>Statement是SQL语句的代表对象</p>
<pre><code class="Java">  Statement stmt = conn.createStatement();

  // executeUpdate() 主要用来执行会改变数据库内容的SQL，返回int结果，表示数据表动的笔数
  stmt.executeUpdate(&quot;INSERT INTO t_message VALUES(1, &#39;justin&#39;, &quot; +
          &quot;&#39;justin@mail.com&#39;, &#39;message...&#39;)&quot;);

  // executeQuery() 查询，返回java.sql.ResultSet对象代表查询的结果，查询的结果会是一笔一笔的数据，可以使用ResultSet的next()来移动至下一笔数据，它会返回true或false表示是否有下一笔数据，接着可以使用getXXX()来取得数据。依据字段名称，或是依据字段顺序取得数据
  //getString(),getInt(),getFloat(),getDouble()

  ResultSet result = stmt.executeQuery(&quot;SELECT * FROM t_message&quot;);
  while(result.next()) {
      int id = result.getInt(&quot;id&quot;);
      String name = result.getString(&quot;name&quot;);
      String email = result.getString(&quot;email&quot;);
      String msg = result.getString(&quot;msg&quot;);
  }

  // 使用查询结果的字段顺序来显示结果的方式(索引是从1开始)
  ResultSet result = stmt.executeQuery(&quot;SELECT * FROM t_message&quot;);
  while(result.next()) {
      int id = result.getInt(1);
      String name = result.getString(2);
      String email = result.getString(3);
      String msg = result.getString(4);
  }

  // 如果事先无法知道是查询还是更新，可以使用execute()
  if(stmt.execute(sql)) {// 返回true，表示查询
      ResultSet rs = stmt.getResultSet();// 取得查询结果 ResultSet
  }else {// 这个是更新操作
      int updated = stmt.getUpdateCount();// 取得更新笔数
  }

  // statement或ResultSet在不使用时，可以使用close()将之关闭，以释放相关资源，Statement关闭时，所关联的ResultSet也会自动关闭。
</code></pre>
</li>
<li><p>留言板</p>
<pre><code class="Java">  // JDBCDemo GuestBookBeam.java
  import java.sql.*;
  import java.util.*;
  import java.io.*;

  public class GuestBookBean implements Serializable {
      private String jdbcUrl = &quot;jdbc:mysql://localhost:3306/demo&quot;;
      private String username = &quot;root&quot;;
      private String password = &quot;123456&quot;;
      public GuestBookBean() {
          try {
              Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
          }catch(ClassNotFoundException ex) {
              throw new RuntimeException(ex);
          }
      }
      public void setMessage(Message message) {
          Connection conn = null;
          Statement statement = null;
          SQLException ex = null;
          try {
              conn = DriverManager.getConnection(jdbcUrl, userName, password);
              statement = conn.createStateMent();
              statement.executeUpdate(&quot;INSET INTO t_message(name, email, msg) VALUES (&#39;&quot;
                                      + message.getName() + &quot;&#39;, &#39;&quot;
                                      + message.getEmail() + &quot;&#39;, &#39;&quot;
                                      + message.getMsg() + &quot;&#39;)&quot;);
          }catch(SQLException e) {
              if(statement != null) {
                  try {
                  statement.close();
                  }catch(SQLException e) {
                      if(ex == null) {
                          ex = e;
                      }
                  }
              }
              if(conn != null) {
                  try {
                      conn.close();
                  }catch(SQLException e) {
                      if(ex == null) {
                          ex = e;
                      }
                  }
              }
              if(ex != null) {
                  throw new RuntimeException(ex);
              }
          }
      }
      public List&lt;Message&gt; getMessage() {
          Connection conn = null;
          Statement statement = null;
          ResultSet result =null;
          SQLException ex = null;
          List&lt;Message&gt; messages = null;
          try {
              conn = DriverManager.getConnection(jdbcUrl, username, password);
              statement = conn.createStatement();
              result.statement.executeQuery(&quot;SELECT * FROM t_message&quot;);
              messages = new ArrayList&lt;Message&gt;();
              while(result.next()) {
                  Message message = new Message();
                  message.setId(result.getLong(1));
                  message.setName(result.getName(2));
                  message.setEmail(result.getEmail(3));
                  message.setMsg(result.getString(4));
                  message.add(message);
              }catch(SQLException e) {
                  ex = e;
              } finally {
                  if(statement != null) {
                      try {
                          statement.close();
                      }catch(SQLException e) {
                          if(ex == null) {
                              ex = e;
                          }
                      }
                  }
                  if(conn != null) {
                      try {
                          conn.close();
                      }catch(SQLException e) {
                          if(ex == null) {
                              ex = e;
                          }
                      }
                  }
                  if(ex != null) {
                      throw new RuntimeException(ex);
                  }
              }
              return messages;
          }
      }
  }
</code></pre>
</li>
<li><p>JDBC规范提到关闭Connection时，一并关闭关联的Statement，但最好留意是否真的关闭了资源，自行关闭Statement是比较保险的。</p>
<pre><code class="JSP">  &lt;%@page contentType=&quot;text/html&quot; pageEncoding=&quot;UTF-8&quot;%&gt;
  &lt;%@taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&gt;
  &lt;c:set target=&quot;${pageContext.request}&quot; property=&quot;characterEncoding&quot; value=&quot;UTF-8&quot;/&gt;
  &lt;jsp:useBean id=&quot;guestbook&quot; class=&quot;cc.openhome.GuestBookBean&quot; scope=&quot;application&quot;/&gt;// application 这样只有在第一次请求时会创建GuestBook Bean，之后GuestBookBean实例就存在应用程序范围中。
  &lt;c:if test=&quot;${param.msg != null}&quot;&gt;
      &lt;jsp:useBean id=&quot;newMessage&quot; class=&quot;cc.openhome.Message&quot;/&gt;
      &lt;jsp:setProperty name=&quot;newMessage&quot; property=&quot;*&quot;/&gt;
      &lt;c:set target=&quot;${guestbook}&quot; property=&quot;message&quot; value=&quot;${newMessage}&quot;/&gt;
  &lt;/c:if&gt;
  &lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot; &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;
  &lt;html&gt;
      &lt;head&gt;
          &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot;&gt;
          &lt;title&gt;访客留言板&lt;/title&gt;
      &lt;/head&gt;
      &lt;body&gt;
          &lt;table style=&quot;text-align: left; width: 100%;&quot; border=&quot;0&quot; cellpadding=&quot;2&quot; cellspacing=&quot;2&quot;&gt;
              &lt;tbody&gt;
                  &lt;c:forEach var=&quot;message&quot; items=&quot;${guestbook.messages}&quot;&gt;
                      &lt;tr&gt;
                          &lt;td&gt;${message.name}&lt;/td&gt;
                          &lt;td&gt;${message.email}&lt;/td&gt;
                          &lt;td&gt;${message.msg}&lt;/td&gt;
                      &lt;/tr&gt;
                  &lt;/c:forEach&gt;
              &lt;/tbody&gt;
          &lt;table&gt;
      &lt;/body&gt;
  &lt;/html&gt;
</code></pre>
</li>
</ul>
<h3 id="使用PreparedStatement-CallableStatement"><a href="#使用PreparedStatement-CallableStatement" class="headerlink" title="使用PreparedStatement,CallableStatement"></a>使用PreparedStatement,CallableStatement</h3><ul>
<li>有部分是动态的数据，必须使用+运算符串接字符串以组成完整的SQL语句。</li>
<li><p>如果有些操作只是SQL语句中某些参数会有不同，其余的SQL子句皆相同。</p>
<ul>
<li><p>java.sql.PreparedStatedStatement</p>
<pre><code class="Java">// 创建一个预编译(precompile)的SQL语句,当中参数会变动的部分，先指定“？”这个占位字符。
PreparedStatement stmt = conn.prepareStatement(&quot;INSERT INTO t_message VALUES(?, ?, ?, ?)&quot;);
stmt.setInt(1, 2);
stmt.setString(2, &quot;momor&quot;);
stmt.setString(3, &quot;momor@mail.com&quot;);
stmt.setString(4, &quot;message2...&quot;);
stmt.executeUpdate(); // 要让SQL执行生效，需执行executeUpdate()或executeQuery()方法。
stmt.clearParameters(); // 清除所设置的参数，之后就可以再使用这个PreparedStatement实例。
</code></pre>
<ul>
<li>在驱动程序支持的情况下，使用PreparedStatement，可以将SQL语句预编译为数据库的运行命令。</li>
<li>使用PreparedStatement在安全也可以有点贡献。以串接的方式组合SQL语句基本上就会有SQL Injection的隐患。</li>
</ul>
</li>
</ul>
</li>
<li><p>如果编写数据库的存储过程(Store Procedure)，并想使用JDBC调用，则可以使用java.sql.CallableStatement</p>
<pre><code class="Java">// 基本语法
{?= call &lt;程序名称&gt;[&lt;自定义量1&gt;, &lt;自定义量2&gt;, ...]}
{call &lt;程序名称&gt;[&lt;自定义量1&gt;, &lt;自定义量2&gt;, ...]}
// 必须调用prepareCall()创建CallableStatement时异常
// 可以使用registerOutParameter()注册输出参数。
</code></pre>
</li>
<li><p>在使用PreparedStatement或CallableStatement时，必须注意SQL类型与Java数据类型的对应。</p>
</li>
<li>在JDBC中,日期是java.sql.Date,时间是java.sql.Time</li>
</ul>
<h2 id="JDBC进阶"><a href="#JDBC进阶" class="headerlink" title="JDBC进阶"></a>JDBC进阶</h2><h3 id="使用DataSource取得连接"><a href="#使用DataSource取得连接" class="headerlink" title="使用DataSource取得连接"></a>使用DataSource取得连接</h3><ul>
<li>重复利用取得的Connection实例，是改善数据库连接性能的一个方式。</li>
<li><p>在Java应用程序中可以通过JNDI(Java Naming Directory Interface)来取得所需的资源对象。</p>
<pre><code class="Java">try {
  Context initContext = new InitialContext();
  Context envContext = (Context) initContext.lookup(&quot;java:/comp/env&quot;);
  dataSource = (DataSource) envContext.lookup(&quot;jdbc/demo&quot;); // 查找jdbc/demo对应的DataSour
}catch (NamingException ex) {
  ...
}

// JDBCDemo web.xml
&lt;web-app&gt;
  &lt;resource-ref&gt;
    &lt;resource-ref&gt;
      &lt;res-ref-name&gt;jdbc/demo&lt;/res-ref-name&gt;
      &lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;
      &lt;res-auth&gt;Container&lt;/res-auth&gt;
      &lt;res-sharing-scope&gt;Shareable&lt;/res-sharing-scope&gt;
    &lt;/resource-ref&gt;
  &lt;/resource-ref&gt;
&lt;/web-app&gt;

// JDBCDemo context.xml //必须在META-INF中 
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UFT-8&quot;?&gt;
&lt;Context antiJARLocking=&quot;true&quot; path=&quot;/JDBCDemo&quot;&gt;
  &lt;Resource name=&quot;jdbc/demo&quot;
    auth=&quot;Container&quot; type=&quot;javax.sql.DataSource&quot;
    maxActive=&quot;100&quot; maxIdle=&quot;30&quot; maxWait=&quot;10000&quot; username=&quot;root&quot;
    password=&quot;123456&quot; driverClassName=&quot;com.mysql.jdbc.Driver&quot;
    url=&quot;jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;amp;characterEncoding=UTF8&quot;/&gt;
    // XML中编写，&amp;必须使用&amp;amp;取代
&lt;/Context&gt;
</code></pre>
</li>
<li><p>其他属性，则是与DBCP(Database Connection Pool),这是内置在Tomcat中的连接池机制</p>
</li>
</ul>
<h3 id="使用ResultSet卷动，跟新数据"><a href="#使用ResultSet卷动，跟新数据" class="headerlink" title="使用ResultSet卷动，跟新数据"></a>使用ResultSet卷动，跟新数据</h3><ul>
<li><p>从JDBC2.0开始，ResultSet并不仅使用previous(),first(),last()等方法前后移动数据光标，还可以调用updateXXX(),updateRow()等方法进行数据修改。</p>
</li>
<li><p>在使用Connection的createStatement()或prepareStatement()方法创建Statement或PrepareStatement实例时，可以指定结构集与并行方式：</p>
<pre><code class="Java">createStatement(int resultSetType, int resultSetConcurrency)
prepareStatement(String sql, int resultSetType, int resultSetCoucurrency)

// 结果集类型可以指定三种设置：
ResultSet.TYPE_FORWARD_ONLY(默认) // ResultSet就只能前进数据光标
ResultSet.TYPE_SCROLL_INSENSITIVE // ResultSet可以前后移动数据光标，不会反应数据库中的数据修改
ResultSet.TYPE_SCRILL_SENSITIVE // ResultSet可以前后移动数据光标，会反应数据库中的数据库修改。

// 更新设置可以有两种指定：
ResultSet.CONCUR_READ_ONLY(默认) // 只能用ResultSet进行属于读取，无法进行更新。
ResultSet.CONCUR_UPDATABLE // 可以使用ResultSet进行数据更新

PreparedStatement stmt = conn.prepareStatement(&quot;SELECT * FROM t_message&quot;，
                        ResultSet.TYPE_SCROLL_INSENSITIVE,
                        ResultSet.CONCUR_UPDATABLE);
</code></pre>
</li>
<li><p>在数据光标移动的API上，可以使用absolute(),afterLast(),beforeFirst(),first(),last()进行绝对位置移动，使用relative(),previous(),next()进行相对位置移动，成功返回true。isAfterLast(),isBeforeFirst(),isFirst(),isLast()判断目前位置。</p>
<pre><code class="Java">Statement stmt = conn.createStatement(&quot;SELECT * FROM t_message&quot;,
                  ResultSet.TYPE_SCROLL_INSENSITIVE,
                  ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery();
rs.absolute(2); // 移至第二行
rs.next(); // 移至下一行
rs.first(); // 移至第一行
boolean b1 = rs.isFirst(); // b1是true
</code></pre>
</li>
<li><p>如果使用ResultSet进行数据修改，则有些条件限制：</p>
<ul>
<li>必须选择单一表格</li>
<li>必须选择主键</li>
<li>必须选择所有NOT NULL的值</li>
</ul>
<pre><code class="Java">// 取得ResultSet之后要进行数据更新，必须移动至要更新的行(Row)，调用UpdateXxx()方法，而后调用updateRow()方法完成更新。
// 如果调用cancelRowUpdates()可取消更新，但必须在调用updateRow()前进行更新的取消
Statement stmt = conn.prepareStatement(&quot;SELECT * FROM t_message&quot;,
                  ResultSet_TYPE_SCROLL_INSENSITIVE,
                  ResultSet_CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery();
rs.absolute(3);
rs.updateString(3, &quot;caterpillar@openhome.cc&quot;);
rs.updateRow();

// 取得ResultSet后想直接进行数据的新增，则要先调用moveToInsertRow()，之后调用updateXxx()设置要新增的数据各个字段，然后调用insertRow()新增数据。
Statement stmt = conn.prepareStatement(&quot;SELECT * FROM t_message&quot;,
                  ResultSet.TYPE_SCROLL_INSENSITIVE,
                  ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery();
rs.moveToInsertRow();
rs.updateString(2, &quot;momor&quot;);
rs.updateString(3, &quot;momor@openhome.cc&quot;);
rs.updateString(4, &quot;blah..blah&quot;);
rs.insertRow();
rs.moveToCurrentRow();

// 如果取得ResultSet后想直接进行数据的删除，则要移动光标至想删除的列，调用deleteRow()删除数据列
Statement stmt = conn.prepareStatement(&quot;SELECT * From t_message&quot;,
                  ResultSet.Type_SCROLL_INSENSITIVE,
                  ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery();
rs.absolute(3);
rs.deleteRow();
</code></pre>
</li>
</ul>
<h3 id="批次更新"><a href="#批次更新" class="headerlink" title="批次更新"></a>批次更新</h3><ul>
<li><p>如果必须对数据库进行大量数据更新，while循环，每一次执行executeUpdate()，其实都会向数据库发送于此SQL。网络传送信息实际上必须启动I/O，进行路由等动作，这样进行大量更新，性能上其实不好。</p>
<ul>
<li><p>使用addBatch()方法收集SQL，并使用executeBatch()方法将所有收集的SQL传送出去。</p>
<pre><code class="Java">Statement stmt = conn.createStatement();
while(someCondition) {
  stmt.addBatch(
    &quot;INSERT INTO t_message(name, email, msg) VALUES(&#39;...&#39;, &#39;...&#39;, &#39;...&#39;)&quot;
  );
}
stmt.executeBatch();

// MySQL驱动程序的Statement实现为例，addBatch()使用了ArrayList来收集SQL。
public synchronized void addBatch(String sql) throws SQLException {
  if (this.batchedArgs == null) {
    this.batchedArgs = new ArrayList();
  }
  if (sql != null) {
    this.batchedArgs.add(sql);
  }
}
// 所有收集的SQL，最后会串为一句SQL，然后传送给数据库。
</code></pre>
</li>
</ul>
</li>
<li><p>批量更新仅用在更新操作。所以批量更新的限制是，SQL不能SELECT，否则会抛出异常。</p>
</li>
<li><p>使用executeBatch时，SQL的执行顺序就是addBatch()时的顺序，executeBatch()会返回int[]，代表每笔SQL造成的数据异动列数。</p>
<ul>
<li>执行executeBatch()时，先前已打开ResultSet会被关闭，执行过后收集SQL用的List会被清空，任何的SQL错误会抛出BatchUpdateException，可以使用这个对象的getUpdateCounts()取得int[]。</li>
</ul>
<pre><code class="Java">PreparedStatement stmt = conn.prepareStatement(
  &quot;INSERT INTO t_message(name, email, msg) VALUES(?, ?, ?)&quot;
);
while(someCondition) {
  stmt.setString(1, &quot;..&quot;);
  stmt.setString(2, &quot;..&quot;);
  stmt.setString(3, &quot;..&quot;);
  stmt.addBatch(); // 收集参数
}
stmt.executeBatch(); // 送出所有参数

// PreparedStatement的addBatch()会收集占位字符真正的数值。
// 以MySQL的PreparedStatement实现类
public void addBatch() throws SQLException {
  if (this.batchedArgs == null) {
    this.batchedArgs == new ArrayList();
  }
  this.batchedArgs.add(new BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
</code></pre>
</li>
</ul>
<h3 id="Blob与Clob"><a href="#Blob与Clob" class="headerlink" title="Blob与Clob"></a>Blob与Clob</h3><ul>
<li><p>如果要将文件写入数据库，可以在数据库表格字段上使用下面的数据类型。</p>
<ul>
<li>BLOB全名(Binary Large Object)，用于储存大量的二进制数据，如图片，影音文件等。</li>
<li>CLOB全名(Character Large Object)，用于储存大量的文字数据。</li>
</ul>
<pre><code class="Java">// 存储
InputStream in = readFileAsInputStream(&quot;...&quot;);
PreparedStatement stmt = conn.prepareStatement(
  &quot;INSERT INTO IMAGES(src, img) VALUE(?, ?)&quot;
);
stmt.setString(1, &quot;...&quot;);
stmt.setBinaryStream(2, in);
stmt.executeUpdate();

// 取得
PreparedStatement stmt = conn.prepareStatement (
  &quot;SELECT img FORM IMAGES&quot;
);
ResultSet rs = stmt.executeQuery();
while(rs.next()) {
  InputStream in = rs.getBinaryStream(1);
  ..
}
</code></pre>
</li>
<li><p>例子，用户上传文件储存到数据库，下载或删除数据库中的文件。</p>
</li>
</ul>
<h3 id="事务简介"><a href="#事务简介" class="headerlink" title="事务简介"></a>事务简介</h3><ul>
<li><p>事务基本要求是原子性(Atomicity)，一致性(Consistency)，隔离行为(Isolation behavior)与持续性(Durability)，简称ACID。</p>
<ul>
<li>原子性：一个事务是一个单元工作，当中可以包括数个步骤(数个SQL)。要开始一个事务边界(通常是以一个BEGIN的命令开始)所有SQL语句下达之后，COMMIT确认所有操作变更，事务成功。若某个SQL错误，ROLLBACK进行撤销动作，事务失败。</li>
<li>一致性：事务作用的数据集合在事务前后必须一致。若事务成功，整个数据集合都必须是事务操作后的状态；若事务失败，整个数据必须与开始事务前一样没有变更，不能发生整个数据集合部分变更，部分没变更。</li>
<li>隔离行为：事务与事务之间，必须互不干扰。</li>
<li>持续性：事务一旦成功，所有变更必须保存下来，即使系统故障，事务的结果也不能遗失。着通常需要系统软，硬件架构支持。</li>
</ul>
<pre><code class="Java">Connection conn = null;
try {
  conn.dataSource.getConnection();
  conn.setAutoCommit(false); // 取消自动提交，提示数据库启始事务
  Statement stmt = conn.createStatement();
  stmt.executeUpdate(&quot;INSERT INTO ...&quot;);
  stmt.executeUpdate(&quot;INSERT INTO ...&quot;);
  conn.commit(); // 提交
}
catch(SQLException e) {
  e.printStackTrace();
  if(conn != null) {
    try {
      conn.rollback(); // 回滚
    }
    catch(SQLException ex) {
      ex.printStackTrace();
    }
  }
}
finally {
  ...
  if(conn != null) {
    try {
      conn.setAutoCommit(true); // 恢复自动提交
      conn.close();
    }
    catch(SQLException ex) {
      ex.printStackTrace();
    }
  }
}

// 仅想要撤回某个SQL执行点，则可以设置储存点(Save Point)
Savepoint point = null;
try {
  conn.setAutoCommit(false);
  Statement stmt = conn.createStatement();
  stmt.executeUpdate(&quot;INSERT INTO ...&quot;);
  ...
  point = conn.setSavepoint(); // 设置存储点
  stmt.executeUpdate(&quot;INSERT INTO ...&quot;);
  ...
  conn.commit();
}
catch(SQLException e) {
  e.printStackTrace();
  if(conn != null) {
    try {
      if(point == null) {
        conn.rollback();
      }else {
        conn.rollback(point); // 撤回储存点
        conn.releaseSavepoint(point); // 释放储存点
      }
    }
    catch(SQLException ex) {
      ex.printStackTrace();
    }
  }
}
finally {
  ...
  if(conn != null) {
    try {
      conn.setAutoCommit(true);
      conn.close();
    }
    catch(SQLException ex) {
      ex.printStackTrace();
    }
  }
}

// 批量更新，不用每一笔都确认，也可以搭配事务管理
try {
  conn.setAutoCommit(false);
  stmt.conn.createStatement();
  while(someCondition) {
    stmt.addBatch(&quot;INSERT INTO ...&quot;);
  }
  stmt.executeBatch();
  conn.commit();
}
...
</code></pre>
</li>
<li><p>数据表格必须支持事务，才可以执行以上所提到的功能。</p>
</li>
<li><p>隔离行为，JDBC可以通过Connection的getTransactionIsolation()取得数据库目前的隔离行为设置，通过setTransactionIsolation()可提示数据库设置指定的隔离行为。可设置的常数是定义在Connection：</p>
<ul>
<li>TRANSACTION_NONE // 表示对事务不隔离行为，仅适用于没有事务功能，以只读功能为主，不会发生同时修改字段的数据库。有事务功能的数据库，可能不理会TRANSACTION_NONE的设置提示</li>
<li>TRANSACTION_UNCOMMITTED</li>
<li>TRANSACTION_COMMITTED</li>
<li>TRANSACTION_REPEATABLE_READ</li>
<li>TRANSACTION_SERIALIZABLE</li>
</ul>
</li>
<li><p>多个事务并行时，可能引发的数据不一致问题</p>
<ul>
<li><p>更新遗失(Lost update)</p>
<ul>
<li>基本上就是指某个事务对字段进行更新的消息，因另一个事务的介入而遗失更新效力</li>
<li>设置隔离层级为“可读取未确认”，A事务未确认的数据，B数据仅可作读取动作。</li>
<li>JDBC通过Connection的setTransactionIsolation()设置为TRANSACTION_UNCOMMITTED</li>
<li>A事务在更新但未确认，延后B事务的更新需求至A事务确认之后。</li>
</ul>
</li>
<li><p>脏读(Dirty read)</p>
<ul>
<li>两个事务同时进行，其中一个事务更新数据但未确认，另一个事务就读取数据，就有可能发生脏读，也就是读到的数据，不干净，不正确。</li>
<li>设置“可读取确认”-TRANSACTION_COMMITTED</li>
<li>读取的事务不会阻止其他事务，未确认的更新事务会阻止其他事务</li>
<li>这个做法对性能影响比较大，另一个做法是事务正在更新但尚未确认前先操作暂存表格，其他事务就不至于读取不正确的数据。</li>
</ul>
</li>
<li><p>无法重复的读取(Unrepeatable read)</p>
<ul>
<li>某个事物两次读取同一个字段的数据并不一致。</li>
<li>设置“可重复读取”-TRANSTION_REPEATAVBLE</li>
<li>读取事务在确认前不阻止其他读取事务，但会阻止其他更新事务</li>
<li>这个做法影响性能较大，另一个组做法是事务正在读取但尚未确认前，另一个事务会在暂存表格上更新。</li>
</ul>
</li>
<li><p>幻读(Phantom read)</p>
<ul>
<li>同一个事务期间，读取到的数据笔数不一致。</li>
<li>设置“可循环序”-TRANSTON_SERIALIZABLE</li>
<li>事务若真的一个一个循环进行，对数据库卡的影响性能过于巨大，实际也未必直接阻止其他事务或真的循序进行，例如采用暂存表格方式。</li>
</ul>
</li>
</ul>
</li>
<li><p>各个隔离行与可预防的问题</p>
<ul>
<li>可读取未确认：预防更新遗失</li>
<li>可读取确认：预防更新遗失，脏读</li>
<li>可重复读取：预防更新遗失，脏读，无法重复的读取</li>
<li>可循序：预防更新遗失，脏读，无法重复的读取，幻读</li>
</ul>
</li>
<li><p>JDBC得知数据库是否支持某个隔离行为设置</p>
<pre><code class="Java">  Databasemetadata meta = conn.getMetaData();
  boolean isSupported = meta.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED);
</code></pre>
</li>
</ul>
<h3 id="metadata简介"><a href="#metadata简介" class="headerlink" title="metadata简介"></a>metadata简介</h3><ul>
<li>Metadata即“关于数据的数据”(Data about data)</li>
<li>DatabaseMetaData Connection.getMetadata() // 数据库整体信息</li>
<li><p>ResultSEtMetaData ResultSet.getMetaData() // 字段名称，字段类型</p>
<pre><code class="Java">  // JDBCDemo TFileInfo.java
  public class TFilesInfo implements serializable {
      private DataSource dataSource;
      public TFiliesInfo() {
          try {
              Context initContexte = new InitialContext();
              Context envContext = (Context) initContext.lookup(&quot;jdbc/demo&quot;);
              dataSource = (DataSource) envContext.lookup(&quot;jdbc/demo&quot;);
          }
          catch(NamingException ex) {
              throw new RuntimeException(ex);
          }
      }
      public List&lt;ColumnInfo&gt; getAllColumnInfo() {
          Connection conn = null;
          ResultSet crs = null;
          SQLException ex = null;
          List&lt;ColumnInfo&gt; infos = null;
          try {
              conn.dataSource.getConnection();
              DatabaseMetaData meta = conn.getMEtaData();
              crs = meta.getAllColumns(&quot;demo&quot;, null, &quot;t_files&quot;, null);
              infos = new ArrayList&lt;ColumnInfo&gt;(); // 收集字段信息
              while(crs.next()) {
                  ColumnInfo info = new ColumnInfo();
                  info.setName(crs.getString(&quot;COLUMN_NAME&quot;));
                  info.setType(crs.getString(&quot;TYPE_NAME&quot;));
                  info.setSize(crs.getInt(&quot;COLUMN_SIZE&quot;));
                  info.setNullable(crs.getBoolean(&quot;IS_NULLABLE&quot;));
                  infos.add(info);
              }
              catch(SQLException e) {
                  ex = e;
              }
              finally {
                  if(conn != null) {
                      try {
                          conn.close();
                      }catch (SQLException e) {
                          if(ex == null) {
                              if(ex == null) {
                                  ex = e;
                              }
                          }
                      }
                  }
              }
              if(ex != null) {
                  throw new RuntimeException(ex);
              }
              return infos;
          }
      }
  }
</code></pre>
</li>
</ul>
<h3 id="RowSet简介"><a href="#RowSet简介" class="headerlink" title="RowSet简介"></a>RowSet简介</h3><ul>
<li>javax.sql.RowSet接口，用以代表数据的的行集合。这里的数据并不一定是数据库中的数据，可以是试算表数据，XML数据或任何具有行集合概念的数据源。</li>
<li>Row定义了行集合基本行为<ul>
<li>JdbcRowSet</li>
<li>CachedRowSet</li>
<li>FilteredRowSet</li>
<li>JoinRowSet</li>
<li>WebRowSet</li>
</ul>
</li>
</ul>
<h2 id="使用SQL标签库"><a href="#使用SQL标签库" class="headerlink" title="使用SQL标签库"></a>使用SQL标签库</h2><h3 id="数据源，查询标签"><a href="#数据源，查询标签" class="headerlink" title="数据源，查询标签"></a>数据源，查询标签</h3><pre><code class="JSP">&lt;%@taglib prefix=&quot;sql&quot; uri=&quot;http://java.sun.com/jsp/jstl/sql%&gt;
&lt;sql:setDataSource dataSource=&quot;java:/comp/env/jdbc/demo&quot;/&gt;
&lt;sql:setDataSource driver=&quot;com.musql.jdbc.Driver&quot; user=&quot;root&quot; password=&quot;123456&quot; url=&quot;jdbc:mysql:3306/demo&quot;/&gt;

&lt;sql:query sql=&quot;SELECT * FROM t_message&quot; var=&quot;messages&quot;/&gt;
</code></pre>
<h3 id="更新，参数，事务标签"><a href="#更新，参数，事务标签" class="headerlink" title="更新，参数，事务标签"></a>更新，参数，事务标签</h3><pre><code class="JSP">&lt;sql:update&gt;
    INSERT INTO t_message(name, email, msg)
        VALUES(&#39;Justin&#39;, &#39;caterpillar@openhome.cc&#39;, &#39;This is a test!&#39;)
&lt;/sql:update&gt;

&lt;sql:update&gt;
    INSERT INTO t_message(name, email, msg) VALUES(?,?,?)
    &lt;sql:param values=&quot;${param.name}&quot;/&gt;
    &lt;sql:param values=&quot;${param.email}&quot;/&gt;
    &lt;sql:param values=&quot;${param.email}&quot;/&gt;
&lt;/sql:update&gt;

// 日期时间格式&lt;sql:paramDate&gt;
// 事务隔离&lt;sql:tramsaction&gt;
</code></pre>
<h1 id="Web容器安全管理"><a href="#Web容器安全管理" class="headerlink" title="Web容器安全管理"></a>Web容器安全管理</h1><h1 id="JavaMail入门"><a href="#JavaMail入门" class="headerlink" title="JavaMail入门"></a>JavaMail入门</h1><h1 id="从模式到框架"><a href="#从模式到框架" class="headerlink" title="从模式到框架"></a>从模式到框架</h1><h2 id="认识设计模式"><a href="#认识设计模式" class="headerlink" title="认识设计模式"></a>认识设计模式</h2><h3 id="Template-Method模式-Gof设计模式"><a href="#Template-Method模式-Gof设计模式" class="headerlink" title="Template Method模式(Gof设计模式)"></a>Template Method模式(Gof设计模式)</h3><ul>
<li>父类会在某方法中定义服务流程。</li>
</ul>
<h3 id="Intercepting-Filter模式-Java-EE-设计模式"><a href="#Intercepting-Filter模式-Java-EE-设计模式" class="headerlink" title="Intercepting Filter模式(Java EE 设计模式)"></a>Intercepting Filter模式(Java EE 设计模式)</h3><ul>
<li>过滤器</li>
</ul>
<h3 id="Model-View-Controller模式-架构模式"><a href="#Model-View-Controller模式-架构模式" class="headerlink" title="Model-View-Controller模式(架构模式)"></a>Model-View-Controller模式(架构模式)</h3><h2 id="重构，模式与框架"><a href="#重构，模式与框架" class="headerlink" title="重构，模式与框架"></a>重构，模式与框架</h2><h3 id="Business-Delegate-委托-模式"><a href="#Business-Delegate-委托-模式" class="headerlink" title="Business Delegate(委托)模式"></a>Business Delegate(委托)模式</h3><ul>
<li>BusinessDelegate角色接受客户端请求，委托BusinessService执行后传回结果给客户端。Business Delegate模式可以有效地隐藏后台程序的复杂度，降低前台界面组件与后台业务组件的耦合度。</li>
</ul>
<h3 id="Service-Locator-定位器-模式"><a href="#Service-Locator-定位器-模式" class="headerlink" title="Service Locator(定位器)模式"></a>Service Locator(定位器)模式</h3><ul>
<li>隐藏了创建服务相关对象，查找服务对象的操作等细节，降低了远程服务对象的实体位置，取得方式对目前应用程序的影响。</li>
</ul>
<h3 id="Transfer-转移-Object模式"><a href="#Transfer-转移-Object模式" class="headerlink" title="Transfer(转移) Object模式"></a>Transfer(转移) Object模式</h3><ul>
<li>Transfer Object 本身通常实现时会符合JavaBean规格，以方便需要的组件进行存取。</li>
<li>Transfer Object 可以携带的信息并不一定只是状态，还可以携带指令以简化指定通信协定的需求(可以搭配Gof模式的Command模式来实现)</li>
</ul>
<h3 id="Front-Controller模式"><a href="#Front-Controller模式" class="headerlink" title="Front Controller模式"></a>Front Controller模式</h3><ul>
<li>在MVC设计模式中，控制器担任了处理用户请求参数，调用模式对象并转发视图对象的职责。</li>
<li><p>在处理用户请求参数方面的操作：</p>
<ul>
<li>取得请求参数</li>
<li>验证请求参数</li>
<li>转换请求参数类型</li>
<li>其他请求参数(或许还需要将请求参数封装为窗体对象)</li>
</ul>
</li>
<li><p>在控制器中，如果绝大多数的代码都是在进行这些请求参数的处理动作，应该将这些处理请求的职责集中管理，创建一个前台控制器来专门进行这些共同的请求处理。</p>
</li>
<li><p>Java EE 模式之一。FrontController负责了集中管理所有请求的操作，处理完毕后再委托给Dispatcher对象，由Dispatcher决定调用哪个Action对象或View对象。</p>
<ul>
<li>Action对象是实际决定如何根据请求来调用后台业务逻辑的对象及决定转发对象。</li>
<li>以MVC来区隔职责的话，FrontController,Dispatcher与Action对象都归属于控制器的角色。</li>
</ul>
</li>
<li><p>FrontController</p>
<ul>
<li>Struts1.2 - ActionServlet</li>
<li>JSF - FaceServlet</li>
<li>Spring MVC - DispatcherServlet</li>
<li>struts2 - FilterDispatcher过滤器(FrontController角色未必一定是Servlet)</li>
</ul>
</li>
</ul>
<h3 id="库与框架"><a href="#库与框架" class="headerlink" title="库与框架"></a>库与框架</h3><ul>
<li>框架是半成品</li>
</ul>

 
                <!-- Meta -->
                <div class="post-meta">
                    <hr>
                    <br>
                    <div class="post-tags">
                        
                            

<a href="/tags/Servlet/">#Servlet</a>


                        
                    </div>
                    <div class="post-date">
                        2017 年 03 月 27 日
                    </div>
                </div>
            </div>

            <!-- Comments -->
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <!-- Disqus Comments -->


            </div>
        </div>
    </div>
</article>
</section>

    <!-- Scripts -->
    <!-- jQuery -->
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="//cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<script type="text/javascript">
	hljs.initHighlightingOnLoad();
	console.log('Hexo-theme-hollow designed by zchen9 http://www.chen9.info © 2015-' + (new Date()).getFullYear());
</script>

    <!-- Google Analytics -->
    

</body>

</html>